是否线程安全检查?

Hol*_*olf 11 c# null exception thread-safety

我有一些代码,在新线程上抛出异常,我需要在主线程上确认和处理.为了实现这一点,我通过使用保存抛出异常的字段来共享线程之间的状态.

我的问题是在检查null时是否需要使用锁,因为我在以下代码示例中进行操作?

public class MyClass
{
    readonly object _exceptionLock = new object();
    Exception _exception;

    public MyClass()
    {
        Task.Run(() =>
        {
            while (CheckIsExceptionNull())
            {
                // This conditional will return true if 'something has gone wrong'.
                if(CheckIfMyCodeHasGoneWrong())
                {
                    lock(_exceptionLock)
                    {
                        _exception = new GoneWrongException();
                    }
                }
            }
        });
    }

    bool CheckIsExceptionNull() // Is this method actually necessary?
    {
        lock (_exceptionLock)
        {
            return _exception == null;
        }
    }

    // This method gets fired periodically on the Main Thread.
    void RethrowExceptionsOnMainThread()
    {
        if (!CheckIsExceptionNull())
        {
            lock (_exceptionLock)
            {
                throw _exception; // Does this throw need to be in a lock?
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

另外,在主线程上抛出异常时是否需要使用锁?

Mar*_*ell 9

首先要注意的是,你的代码不是线程安全的,因为你有一个线程竞争:你CheckIsExceptionNull在一个不同的锁定区域中检查你抛出的位置,但是:值可以在测试和throw之间改变.

不保证字段访问是线程安全的.特别是,虽然引用不能 被破坏(保证很多 - 引用的读取和写入是原子的),但由于CPU缓存等原因,不能保证不同的线程会看到最新的值.实际上不太可能咬你,但这是一般情况下线程问题的问题; p

就个人而言,我可能只是让这个领域变得不稳定,并使用本地.例如:

var tmp = _exception;
if(tmp != null) throw tmp;
Run Code Online (Sandbox Code Playgroud)

以上没有线程竞赛.添加:

volatile Exception _exception;
Run Code Online (Sandbox Code Playgroud)

确保该值不会缓存在寄存器中(尽管这在技术上是副作用,而不是预期/记录的效果volatile)

  • 我实际上认为`Volatile.Read()`比制作变量`volatile`更好,因为它更有针对性(你只在你需要它的地方做)并且更明显地表示正在进行易失性读取.(注意``Volatile.Read()`与`Thread.VolatileRead()`不同[不应该使用](http://stackoverflow.com/a/15052688/106159).) (3认同)