是否支持线程安全?

时间:2015-06-15 10:56:26

标签: c# exception null 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?
            }
        }
    }
}

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

2 个答案:

答案 0 :(得分:9)

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

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

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

var tmp = _exception;
if(tmp != null) throw tmp;

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

volatile Exception _exception;

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

答案 1 :(得分:0)

关于这样的问题,可能会有很多有趣且有用的信息。您可以花几个小时阅读有关它的博客文章和书籍。即使是真正了解它的人也不同意。 (正如你在Marc Gravell和Eric Lippert的帖子中看到的那样)。

因此,我对这个问题没有答案。只是一个建议:始终使用锁。只要有多个线程访问一个字段。没有风险,没有猜测,没有讨论编译器和CPU技术。只需使用锁并进行保存,不仅可以在机器上的大多数情况下使用,而且可以在每台机器上使用。

进一步阅读: