Безопасно ли обращаться к этой переменной из нескольких потоков?

Я обычно пишу код, который выглядит следующим образом:

public class MyClass
{
    static bool m_stop = false;

    public static void Main()
    {
        var th = new Thread(DoStuff);

        th.Start();

        Console.ReadLine();

        m_stop = true;

        th.Join();
    }

    private static void DoStuff()
    {
        while( !m_stop )
        {
            // do things here
        }
    }
}

... и я всегда синхронизирую доступ к переменной m_stop, просто по привычке. Мне интересно, действительно ли это необходимо делать (при условии, что запуск дополнительного цикла или двух в DoStuff() ничему не повредит). Что самое худшее может произойти, если код будет выполняться именно так? Я думал об использовании ключевого слова volatile для m_stop, но я не уверен, что даже это необходимо. Кто-нибудь знает наверняка?


person Mark    schedule 22.09.2009    source источник


Ответы (3)


Без изменчивости и без какой-либо синхронизации код, который вы показали, не является потокобезопасным. Один поток может изменить значение переменной, а другой поток может никогда его не увидеть, зацикливаясь до бесконечности. Если вы синхронизируете весь доступ к нему с общей блокировкой, он будет потокобезопасным.

Теперь, волатильность - сложная тема - я думал, что понимал ее до недавнего времени, но это означает не совсем то, что я думал. Однако я почти уверен, что простое изменение переменной сделает то, что вам нужно.

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

person Jon Skeet    schedule 22.09.2009
comment
В примере это не показано, но в OP указано, что они синхронизируют доступ к m_stop. - person Andrew Hare; 22.09.2009
comment
Чтобы лучше понять volatile и путаницу вокруг него, прочитайте это (bluebytesoftware.com/blog/) сообщение Джо Даффи. Действительно отличное чтение. - person MRG; 22.09.2009
comment
ой... Джон, я опоздал на эту ссылку... не заметил эту ссылку. - person MRG; 22.09.2009
comment
@Andrew: На самом деле, прочитав вопрос в третий раз, мой первоначальный ответ все еще был полезен: ОП спрашивал, является ли код в вопросе потокобезопасным, а не код, включающий синхронизацию. - person Jon Skeet; 22.09.2009
comment
@Jon - я согласен, я думаю, что твой ответ тоже полезен - я просто был придирчив :) - person Andrew Hare; 22.09.2009
comment
+1 Кстати, тебе - я думал, что уже проголосовал за тебя, но это не так. - person Andrew Hare; 22.09.2009
comment
Спасибо, Джон, это был именно тот ответ, который я искал. Обычно в подобных ситуациях я делаю полную синхронизацию, и, похоже, это был безопасный вариант. - person Mark; 22.09.2009

Хотя в этом подходе нет ничего технически неправильного, у вас могут возникнуть проблемы, когда вашему приложению потребуется масштабирование. Также вам не нужно делать m_stop изменчивым, если вы синхронизируете доступ к нему.

person Andrew Hare    schedule 22.09.2009
comment
Ваш комментарий прямо противоречит Джону. Я не знаю, кто прав, но это кажется важным моментом. Если доступ к m_stop не синхронизирован, значение всегда будет распространяться на другой поток? - person Mark; 24.09.2009

вы должны установить барьер памяти при обновлении m_stop. поэтому вам придется пометить его как volatile (или самостоятельно установить барьеры памяти / volatile для чтения-записи)

person Community    schedule 22.09.2009