.NET线程安全

时间:2010-08-23 15:32:41

标签: .net thread-safety

ApplicationException构造函数是一个实例成员,因此不保证它是线程安全的。您如何知道以下代码是否完全没必要?当我说“知道”时,我的意思是说文档中有些东西说这是不必要的,或者你看过.NET源代码,所以你知道这是不必要的吗?

// thread-safe
internal static class TsApplicationException
{
    private readonly static object myLock = new object();

    internal static void Throw(string msg, Exception e)
    {
        lock (myLock)
        {
            throw new ApplicationException(msg, e);
        } 
        // the implementation of lock() ensures that the unlock will happen
        // even if there is an exception thrown
    }
}

编辑:此问题并非毫无根据,因为文档明确暗示这可能是必要的。这个问题可能是由一个有点普遍的文档错误引起的。

编辑:下面有人建议除了我的帖子 - 我指的是我直接负责的代码,加上我从未见过的代码,但唉我作为客户间接负责.NET和Windows - 我的计算机上运行了多个线程,如浏览器,Office和音乐播放器,但似乎工作正常。我觉得理想情况下,我正在寻找不同于示例的证明。也许编写我的音乐播放器的人也是这样锁定的;)在这种情况下,它根本就不是证据。

我还想补充一点,这个问题可能有两个变种。一个问题是单个应用程序的线程(发生在.NET和Windows线程中的事情)是健全的,另一个是给定计算机上所有应用程序的所有线程都是声音的问题(彼此共存以及.NET和Windows)。我的问题适用于第一个案例。我没有假设Windows保持应用程序隔离的程度,这不是我发布此问题的关注点。

最后,如果需要此锁定,则.NET框架和第三方对象使用需要许多其他锁定。示例:MySQL文档也有关于“不保证是线程安全的”实例方法的相同行。在几个.NET案例中,可能是大多数MySQL案例中,风险涉及的功能可能远远超过抛出异常。

3 个答案:

答案 0 :(得分:4)

首先,实例成员可以是线程安全的。由于我们之前讨论过的原因,确保静态成员是线程安全的但通常不为实例成员提供相同的保证,这是很常见的。

其次,在多个线程可以访问同一实例的上下文中不调用构造函数。此时,对新构造的ApplicationException的唯一引用是调用方法的本地引用,因此只对一个线程可见。如果两个线程同时触及Throw方法,则它们将创建两个单独的实例。

因此,虽然它本身不是线程安全的,但它不会在多线程可访问的上下文中使用,因此不需要锁定它。

更重要的一个案例是:

void MyFunctionOfThreadSafety(string someStr, int someInt, bool someBool)
{
  if(someBool)// №1
  {
    var dict = new Dictionary<string, int>();
    dict[someStr] = someInt; // №2
    int test = 0;
    if(!dict.TryGetValue(someStr, out test))// №3
      throw new Exception("really unexpected");
    dict[someStr] = test + someInt; // №4
  }
}

在这段代码中,注释为№1到№4的行是如果有问题的对象可以通过多个线程访问的地方,那么线程安全问题可能会导致问题(实际上,除了№1以外的所有提供线程可以切换的一点,事情开始变得奇怪了。

此代码完全是线程安全的。虽然它使用对象做了几个可能不安全的事情,但是这些对象中没有一个可以被另一个线程改变。

someIntsomeBool是未通过byrefout的值类型参数。因此,它们只存在于该方法的上下文中(以及它在byrefout上传递它时调用的方法)。

someStr是作为参数传递的引用类型,这意味着它可能也存储在另一个线程可以获取的位置。但是,因为它是不可变的,所以没有其他线程写入它的危险(因为根本没有线程可以写入它)。读取不必受到保护而不受写入的影响(尽管当您进行读取和写入时,可能必须同时锁定读取和写入)。由于只能在someStr上进行读取操作,所以它是安全的。

dicttest是在此方法中创建的,因此它们是安全的。如果从方法返回dict,那么作为可变引用类型,如果存储在多个线程可见的某个地方,它将成为线程安全问题。但是(a)它没有和(b)在发生之后 之前不会引起关注;无论如何,它都是线程安全的。

答案 1 :(得分:2)

这是不必要的,因为ApplicationException对象中的属性不会被更改,我相信异常对象中的所有属性都是只读的。

因此,线程安全无关紧要。

答案 2 :(得分:0)

构造函数不是线程安全的唯一时间是它们在类中更改静态数据。这是非常罕见的,因此锁定是不必要的。实际上,您只是锁定了构造函数的使用,因此任何未在TsApplicationException中运行的其他线程都可以同时抛出ApplicationException