在C#中是否应该同时使用锁(Locks)和互斥体(Mutexes)?

14 浏览
0 Comments

在C#中是否应该同时使用锁(Locks)和互斥体(Mutexes)?

这是否过度了,并且只需要其中一个?我搜索到了关于C#中互斥和锁的不同帖子,链接在这里这里

示例:

在我们的应用程序中,我们有一个函数,会启动多个重新连接的线程,在这个线程中我们使用了一个Mutex和一个locklock会阻止其他线程对这部分代码的访问,从而防止connect被其他线程更新,不是吗?

bool connect = false;
Mutex reconnectMutex = new Mutex(false, "Reconnect_" + key);
try
{
   lock(site)
   {
      if(site.ContainsKey(key))
      {
         siteInfo = (SiteInfo)site[key];
         if(reconnectMutex.WaitOne(100, true))
         {
            connect = true;
         }
      }
   }
   if (connect)
   { 
      // 处理线程逻辑
   }
}
catch
{}
reconnectMutex.ReleaseMutex();

更多信息:

这是在一个不运行在Web Garden中的ASP.NET WebService中。

0
0 Comments

问题的出现原因是代码中存在一个严重的bug。在代码中,Mutex只有在site.ContainsKey()返回true时才会被获取,但是在最后却无条件地释放了Mutex(reconnectMutex.ReleaseMutex())。因此,如果site.ContainsKey返回false,那么释放Mutex会抛出ApplicationException,因为调用线程并不拥有该Mutex。

解决方法是在释放Mutex之前,先判断Mutex是否被当前线程拥有,可以使用Mutex.OwnedByCurrentThread属性来判断。如果Mutex没有被当前线程拥有,则不进行释放操作,避免抛出异常。

以下是修改后的代码示例:

if (site.ContainsKey())
{
    // Acquire the Mutex
    reconnectMutex.WaitOne();
    try
    {
        // Perform the necessary operations
        // ...
    }
    finally
    {
        // Release the Mutex only if it is owned by the current thread
        if (reconnectMutex.OwnedByCurrentThread)
        {
            reconnectMutex.ReleaseMutex();
        }
    }
}

通过以上修改,可以避免释放未拥有的Mutex而导致的异常。这样,在使用Mutex和Locks的同时,可以确保线程安全性,并避免出现错误和异常。

0
0 Comments

在C#中,可以使用"lock"关键字或Mutex类来实现锁定和互斥。然而,它们在行为上有很大的区别。"lock"实际上只是对Monitor.Enter/Exit的一种语法糖,而Mutex是一种多进程锁。

由于它们的不同行为,有时候需要同时在同一个应用程序或方法中使用它们,因为它们被设计用于阻塞不同的事物。然而,在你的情况下,我认为你最好研究一下Semaphore和Monitor。因为你不需要在进程之间进行锁定,所以它们可能在这种情况下是更好的选择。

解决这个问题的方法是使用Semaphore和Monitor替代Mutex。下面是一个示例代码:

Semaphore semaphore = new Semaphore(1, 1);

Monitor monitor = new Monitor();

// 使用Semaphore实现锁定

semaphore.WaitOne();

try

{

// 执行需要互斥的代码

}

finally

{

semaphore.Release();

}

// 使用Monitor实现锁定

lock (monitor)

{

// 执行需要互斥的代码

}

通过使用Semaphore和Monitor,你可以在代码中实现锁定和互斥,而无需同时使用Mutex和"lock"关键字。这样可以提高代码的可读性和可维护性,同时避免可能出现的问题。

0
0 Comments

互斥体(Mutex)因为有名称,所以阻止了同一台机器上的任何进程访问它,而锁(lock)只会阻止同一进程中的其他线程。从代码示例中,我无法看出为什么需要同时使用这两种锁。持有简单锁的时间应该很短,但重量级的进程间互斥体却被锁定了一个可能更长(尽管有重叠)的时间!直接使用互斥体可能会更简单。也许需要弄清楚是否真的需要进程间锁定。

顺便说一句,在这种情况下使用catch {}是绝对错误的。你应该使用finally { /* 释放互斥体 */ }。它们是非常不同的。catch会捕捉比它应该捕捉的更多种类的异常,并且还会导致响应于底层异常(如内存损坏、访问冲突等)的嵌套finally处理程序执行。所以,不应该这样写:

try
{
    // something
}
catch
{}
// cleanup

而应该这样写:

try
{
    // something
}
finally
{
    // cleanup
}

如果有特定的异常可以恢复,可以捕捉它们:

try
{
    // something
}
catch (DatabaseConfigurationError x)
{
    // 告诉用户正确配置数据库
}
finally
{
    // cleanup
}

互斥体的名称必须根据机器来命名。

好的,我希望在第一句中已经澄清了这一点。

你是说我应该消除catch语句,并将其替换为finally吗?

是的。如果“处理线程逻辑”的代码抛出了一些可以恢复的异常,那么你可能还需要一个catch,但是你应该只捕捉可以恢复的异常。一般来说,“catch {} Foo();”这样做的方式是错误的,而“finally { Foo(); }”才是设计用来实现这个目的的正确方式。

尽管这超出了重复锁定问题的范围,但我已经更清楚地回答了这个问题,这可能比重复锁定问题更重要!

0