gpt4 book ai didi

C# - 使用互斥锁的锁定问题

转载 作者:行者123 更新时间:2023-11-30 13:14:07 27 4
gpt4 key购买 nike

我有一个 Web 应用程序,它控制哪些 Web 应用程序从我们的负载均衡器获得服务流量。 Web 应用程序在每个单独的服务器上运行。

它跟踪处于 ASP.NET 应用程序状态的对象中每个应用程序的“进入或退出”状态,并且只要状态发生变化,该对象就会被序列化到磁盘上的一个文件中。当 Web 应用程序启动时,状态从文件中反序列化。

虽然站点本身只收到几个请求,并且它很少访问文件,但我发现由于某种原因,在尝试读取或写入文件时发生冲突非常容易。这种机制需要非常可靠,因为我们有一个自动化系统,可以定期对服务器进行滚动部署。

在任何人对上述任何一项的审慎性发表任何评论之前,请允许我简单地说,解释其背后的推理会使这篇文章比现在更长,所以我想避免移动山。

也就是说,我用来控制对文件的访问的代码如下所示:

internal static Mutex _lock = null;
/// <summary>Executes the specified <see cref="Func{FileStream, Object}" /> delegate on
/// the filesystem copy of the <see cref="ServerState" />.
/// The work done on the file is wrapped in a lock statement to ensure there are no
/// locking collisions caused by attempting to save and load the file simultaneously
/// from separate requests.
/// </summary>
/// <param name="action">The logic to be executed on the
/// <see cref="ServerState" /> file.</param>
/// <returns>An object containing any result data returned by <param name="func" />.
///</returns>
private static Boolean InvokeOnFile(Func<FileStream, Object> func, out Object result)
{
var l = new Logger();
if (ServerState._lock.WaitOne(1500, false))
{
l.LogInformation( "Got lock to read/write file-based server state."
, (Int32)VipEvent.GotStateLock);
var fileStream = File.Open( ServerState.PATH, FileMode.OpenOrCreate
, FileAccess.ReadWrite, FileShare.None);
result = func.Invoke(fileStream);
fileStream.Close();
fileStream.Dispose();
fileStream = null;
ServerState._lock.ReleaseMutex();
l.LogInformation( "Released state file lock."
, (Int32)VipEvent.ReleasedStateLock);
return true;
}
else
{
l.LogWarning( "Could not get a lock to access the file-based server state."
, (Int32)VipEvent.CouldNotGetStateLock);
result = null;
return false;
}
}

这通常有效,但有时我无法访问互斥锁(我在日志中看到“无法获得锁定”事件)。我无法在本地重现这个 - 它只发生在我的生产服务器(Win Server 2k3/IIS 6)上。如果我删除超时,应用程序将无限期挂起(竞争条件??),包括后续请求。

当我确实收到错误时,查看事件日志告诉我在记录错误之前,上一个请求已实现并释放互斥锁。

互斥体在 Application_Start 事件中实例化。当它在声明中静态实例化时,我得到相同的结果。

借口,借口:线程/锁定不是我的强项,因为我通常不必担心。

关于为什么它随机无法获得信号的任何建议?

更新:

我已经添加了适当的错误处理(多么令人尴尬!),但我仍然遇到相同的错误 - 并且为了记录,未处理的异常从来都不是问题。

只有一个进程会访问该文件 - 我没有为此应用程序的 Web 池使用 Web 园,也没有其他应用程序使用该文件。我能想到的唯一异常(exception)是当应用程序池回收时,旧 WP 在创建新 WP 时仍处于打开状态 - 但我可以通过观察任务管理器看出问题发生时只有一个工作进程。

@mmr:使用 Monitor 与使用 Mutex 有何不同?根据 MSDN 文档,它看起来似乎在有效地做同样的事情——如果我不能用我的互斥锁获得锁,它会通过返回 false 优雅地失败。

另一件要注意的事情:我遇到的问题似乎完全是随机的 - 如果它在一个请求上失败,它可能在下一个请求中正常工作。似乎也没有模式(当然至少没有其他模式)。

更新 2:

此锁不用于任何其他调用。在 InvokeOnFile 方法之外引用 _lock 的唯一时间是实例化时。

被调用的 Func 要么从文件中读取并反序列化为一个对象,要么序列化一个对象并将其写入文件。这两个操作都不是在单独的线程上完成的。

ServerState.PATH 是一个静态只读字段,我不希望它会导致任何并发问题。

我还想重申我之前的观点,即我无法在本地(在 Cassini 中)重现这一点。

经验教训:
  • 使用正确的错误处理(废话!)
  • 为工作使用正确的工具(并基本了解该工具的作用/方式)。正如 sambo 指出的那样,使用 Mutex 显然有很多开销,这会导致我的应用程序出现问题,而 Monitor 是专门为 .NET 设计的。
  • 最佳答案

    如果您需要 ,您应该只使用互斥锁跨进程同步 .

    Although a mutex can be used for intra-process thread synchronization, using Monitor is generally preferred, because monitors were designed specifically for the .NET Framework and therefore make better use of resources. In contrast, the Mutex class is a wrapper to a Win32 construct. While it is more powerful than a monitor, a mutex requires interop transitions that are more computationally expensive than those required by the Monitor class.



    如果你需要支持进程间锁定,你需要一个 Global mutex .

    正在使用的模式非常脆弱,没有异常处理,您无法确保您的互斥锁被释放。这确实是有风险的代码,很可能是您在没有超时时看到这些挂起的原因。

    此外,如果您的文件操作花费的时间超过 1.5 秒,那么并发互斥锁可能无法获取它。我建议正确锁定并避免超时。

    我认为最好重新编写它以使用锁。此外,看起来您正在调用另一种方法,如果这需要永远,锁定将永远保持。那是相当冒险的。

    这既短又安全:
    // if you want timeout support use 
    // try{var success=Monitor.TryEnter(m_syncObj, 2000);}
    // finally{Monitor.Exit(m_syncObj)}
    lock(m_syncObj)
    {
    l.LogInformation( "Got lock to read/write file-based server state."
    , (Int32)VipEvent.GotStateLock);
    using (var fileStream = File.Open( ServerState.PATH, FileMode.OpenOrCreate
    , FileAccess.ReadWrite, FileShare.None))
    {
    // the line below is risky, what will happen if the call to invoke
    // never returns?
    result = func.Invoke(fileStream);
    }
    }

    l.LogInformation("Released state file lock.", (Int32)VipEvent.ReleasedStateLock);
    return true;

    // note exceptions may leak out of this method. either handle them here.
    // or in the calling method.
    // For example the file access may fail of func.Invoke may fail

    关于C# - 使用互斥锁的锁定问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/448550/

    27 4 0