Что делает метод потокобезопасным? Каковы правила? - PullRequest
140 голосов
/ 24 марта 2012

Существуют ли общие правила / рекомендации для того, что делает метод потокобезопасным?Я понимаю, что, возможно, существует миллион разовых ситуаций, но что в целом?Это так просто?

  1. Если метод обращается только к локальным переменным, он безопасен для потоков.

Это так?Это относится и к статическим методам?

Один ответ, предоставленный @Cybis, был:

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

Это относится и к статическим методам?

Если метод передается ссылочным объектом, нарушает ли это безопасность потока?Я провел некоторые исследования, и есть много примеров определенных случаев, но я надеялся, что смогу определить, используя всего несколько правил, рекомендации, которым нужно следовать, чтобы убедиться, что метод является поточно-ориентированным.

Итак, я предполагаю, что мой окончательный вопрос: «Есть ли короткий список правил, определяющих потокобезопасный метод? Если да, то каковы они?»

РЕДАКТИРОВАТЬ Много хороших замечаний было сделано здесь.Я думаю, что реальный ответ на этот вопрос: «Нет простых правил для обеспечения безопасности потоков».Здорово.Хорошо.Но в целом Я думаю, что принятый ответ дает хорошее, краткое резюме.Всегда есть исключения.Быть по сему.Я могу жить с этим.

Ответы [ 4 ]

129 голосов
/ 24 марта 2012

Если метод (экземпляр или статический) ссылается только на переменные, входящие в этот метод, тогда он является поточно-ориентированным, поскольку каждый поток имеет свой собственный стек:

В этом случае несколько потоков могут одновременно вызывать ThreadSafeMethod без проблем.

public class Thing
{
    public int ThreadSafeMethod(string parameter1)
    {
        int number; // each thread will have its own variable for number.
        number = parameter1.Length;
        return number;
    }
}

Это также верно, если метод вызывает другой метод класса, который ссылается только на локальные переменные:

public class Thing
{
    public int ThreadSafeMethod(string parameter1)
    {
        int number;
        number = this.GetLength(parameter1);
        return number;
    }

    private int GetLength(string value)
    {
        int length = value.Length;
        return length;
    }
}

Если метод обращается к каким-либо свойствам или полям (состояния объекта) (экземпляру или статическому), вам необходимо использовать блокировки, чтобы гарантировать, что значения не будут изменены другим потоком.

public class Thing
{
    private string someValue; // all threads will read and write to this same field value

    public int NonThreadSafeMethod(string parameter1)
    {
        this.someValue = parameter1;

        int number;

        // Since access to someValue is not synchronised by the class, a separate thread
        // could have changed its value between this thread setting its value at the start 
        // of the method and this line reading its value.
        number = this.someValue.Length;
        return number;
    }
}

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

Для обеспечения надлежащего параллелизма вам необходимо использовать блокировку.

для получения дополнительной информации см. оператор блокировки C # ссылка и ReadWriterLockSlim .

блокировка в основном полезна для обеспечения функциональности по одному,
ReadWriterLockSlim полезно, если вам нужно несколько читателей и писателей.

102 голосов
/ 24 марта 2012

Если метод обращается только к локальным переменным, он является потокобезопасным. Это оно?

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

https://stackoverflow.com/a/8883117/88656

Это относится и к статическим методам?

Абсолютно нет.

Один ответ, предоставленный @Cybis, звучал так: «Локальные переменные не могут быть общими для потоков, поскольку каждый поток получает свой собственный стек».

Абсолютно нет. Отличительной характеристикой локальной переменной является то, что она видима только из локальной области действия , а не то, что она выделяется во временном пуле . Это совершенно законно и возможно получить доступ к одной и той же локальной переменной из двух разных потоков. Вы можете сделать это, используя анонимные методы, лямбда-выражения, блоки итераторов или асинхронные методы.

Это относится и к статическим методам?

Абсолютно нет.

Если метод передается эталонному объекту, нарушает ли это безопасность потока?

Может быть.

Я провел некоторые исследования, и есть много примеров определенных случаев, но я надеялся, что смогу определить, используя всего несколько правил, рекомендации, которым нужно следовать, чтобы убедиться, что метод является потокобезопасным.

Вам придется научиться жить с разочарованием. Это очень сложный предмет.

Итак, я думаю, что мой последний вопрос: «Есть ли короткий список правил, определяющих потокобезопасный метод?

Неа. Как вы видели из моего примера ранее , пустой метод может быть не поточно-безопасным . Вы также можете спросить: «Есть ли короткий список правил, который гарантирует, что метод правильный ». Нет, нет. Безопасность потоков - не что иное, как чрезвычайно сложный вид правильности.

Более того, тот факт, что вы задаете вопрос, указывает на ваше фундаментальное неправильное понимание безопасности потоков. Безопасность потоков - это global , а не local свойство программы. Причина, по которой так трудно получить права, заключается в том, что вы должны иметь полное представление о поведении потоков всей программы , чтобы обеспечить ее безопасность.

Опять же, посмотрите на мой пример: каждый метод тривиален . Именно способ, которым методы взаимодействуют друг с другом на «глобальном» уровне, делает программу тупиковой. Вы не можете смотреть на каждый метод и отмечать его как «безопасный», а затем ожидать, что вся программа безопасна, больше, чем вы можете сделать вывод, что, поскольку ваш дом сделан из 100% -го пустотелого кирпича, этот дом также не полый. Пустота дома является глобальной собственностью всего объекта, а не совокупностью свойств его частей.

9 голосов
/ 24 марта 2012

Не существует жесткого и быстрого правила.

Вот некоторые правила, которые делают поток кода безопасным в .NET, и почему это не очень хорошие правила:

  1. Функция и все функцииона должна быть чистой (без побочных эффектов) и использовать локальные переменные.Хотя это сделает ваш код поточно-ориентированным, очень мало интересных вещей, которые можно сделать с этим ограничением в .NET.
  2. Каждая функция, которая работает с общим объектом, должна lock с общимвещь.Все замки должны быть выполнены в одинаковом порядке.Это сделает поток кода безопасным, но он будет невероятно медленным, и вы также можете не использовать несколько потоков.
  3. ...

Нет правила, которое делаетбезопасный поток кода, единственное, что вы можете сделать, это убедиться, что ваш код будет работать независимо от того, сколько раз он активно выполняется, каждый поток может быть прерван в любой точке, причем каждый поток находится в своем собственном состоянии / местоположении, иэто для каждой функции (статической или другой), которая обращается к общим объектам.

4 голосов
/ 24 марта 2012

Он должен быть синхронизирован с использованием блокировки объекта, без сохранения состояния или неизменяемым.

ссылка: http://docs.oracle.com/javase/tutorial/essential/concurrency/immutable.html

...