Подтвердить что ты не робот

Имеются ли проверки на отсутствие потоков?

У меня есть код, в котором исключения набросаются на новый поток, который мне нужно подтвердить и обсудить в главной теме. Для этого я разделяю состояние между потоками, используя поле, которое содержит исключение.

Мой вопрос Нужно ли использовать блокировки при проверке нулевого, как я делаю в следующем примере кода?

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?
            }
        }
    }
}

Кроме того, нужно ли использовать блокировку при выбросе исключения в главный поток?

4b9b3361

Ответ 1

Первое, что нужно отметить, это то, что ваш код не является потокобезопасным, потому что у вас есть гонка потоков: вы проверяете CheckIsExceptionNull в другой заблокированной области, куда вы бросаете, но: значение может меняться между тестом и броском.

Доступ к полям не гарантируется потоком. В частности, в то время как ссылки не могут разорваться (что гарантировано - чтение и запись ссылок являются атомарными), не гарантируется, что в разных потоках будут отображаться последние значения из-за кэширования ЦП и т.д. очень маловероятно, чтобы на самом деле вас укусить, но проблема с проблемами потоковой передачи в общем случае p

Лично я, вероятно, просто сделаю поле неустойчивым и использую локальный. Например:

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

Вышеупомянутая не имеет ничьей гонки. Добавление:

volatile Exception _exception;

гарантирует, что значение не кэшируется в регистре (хотя это технически является побочным эффектом, а не предполагаемым/документированным эффектом volatile)

Ответ 2

Там может быть много интересной и полезной информации о таких вопросах. Вы можете потратить часы на чтение сообщений в блогах и книг об этом. Даже люди, которые действительно знают это, не согласны. (Как вы можете видеть в сообщении от Марка Гравелла и Эрика Липперта).

Итак, у меня нет ответа на этот вопрос. Просто предложение: Всегда использовать блокировки. Как только более один поток обращается к полю. Никакого риска, никаких угадываний, обсуждений о технологии компилятора и процессора. Просто используйте блокировки и сохраняйте их, чтобы они работали не только в большинстве случаев на вашем компьютере, но и в каждом случае на каждой машине.

Дальнейшее чтение: