Что делает метод потокобезопасным? Каковы правила?
Существуют ли общие правила / рекомендации для того, что делает метод потокобезопасным? Я понимаю, что, возможно, существует миллион разовых ситуаций, но что в целом? Это так просто?
- Если метод обращается только к локальным переменным, это потокобезопасно.
Это оно? Это относится и к статическим методам?
Один ответ, предоставленный @Cybis, был:
Локальные переменные не могут быть разделены между потоками, потому что каждый поток получает свой собственный стек.
Это относится и к статическим методам?
Если методу передается ссылочный объект, нарушает ли это безопасность потока? Я провел некоторые исследования, и есть много примеров определенных случаев, но я надеялся, что смогу определить, используя всего несколько правил, рекомендации, которым нужно следовать, чтобы убедиться, что метод является потокобезопасным.
Итак, я предполагаю, что мой главный вопрос: "Есть ли короткий список правил, которые определяют потокобезопасный метод? Если да, то каковы они?"
РЕДАКТИРОВАТЬ
Много хороших замечаний было сделано здесь. Я думаю, что реальный ответ на этот вопрос: "Нет простых правил, обеспечивающих безопасность потоков". Здорово. Хорошо. Но в целом я думаю, что принятый ответ дает хорошее, краткое резюме. Всегда есть исключения. Быть по сему. Я могу жить с этим.
4 ответа
Если метод (экземпляр или статический) ссылается только на переменные, ограниченные областью действия этого метода, он является поточно-ориентированным, поскольку каждый поток имеет свой собственный стек:
В этом случае несколько потоков может вызвать 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
полезно, если вам нужно несколько читателей и писателей.
Если метод обращается только к локальным переменным, это потокобезопасно. Это оно?
Абсолютно нет. Вы можете написать программу, имеющую доступ только к одной локальной переменной из одного потока, который, тем не менее, не является потокобезопасным:
/questions/44660284/napishite-programmu-kotoraya-obyazatelno-vojdet-v-tupik/44660365#44660365
Это относится и к статическим методам?
Точно нет.
Один из ответов, предоставленных @Cybis, звучал так: "Локальные переменные не могут совместно использоваться потоками, поскольку каждый поток получает свой собственный стек".
Точно нет. Отличительной характеристикой локальной переменной является то, что она видна только внутри локальной области, а не то, что она размещена во временном пуле. Это совершенно законно и возможно получить доступ к одной и той же локальной переменной из двух разных потоков. Вы можете сделать это, используя анонимные методы, лямбда-выражения, блоки итераторов или асинхронные методы.
Это относится и к статическим методам?
Точно нет.
Если методу передается ссылочный объект, нарушает ли это безопасность потока?
Может быть.
Я провел некоторые исследования, и есть много примеров определенных случаев, но я надеялся, что смогу определить, используя всего несколько правил, рекомендации, которым нужно следовать, чтобы убедиться, что метод является потокобезопасным.
Вам придется научиться жить с разочарованием. Это очень сложный предмет.
Итак, я предполагаю, что мой главный вопрос: "Есть ли короткий список правил, которые определяют поточно-безопасный метод?
Нету. Как вы видели из моего примера ранее , пустой метод может быть не поточно-ориентированным. Вы также можете спросить: "Есть ли короткий список правил, обеспечивающих корректность метода". Нет, нет Безопасность потоков - не что иное, как чрезвычайно сложный вид правильности.
Более того, тот факт, что вы задаете вопрос, указывает на ваше фундаментальное недопонимание в отношении безопасности потоков. Безопасность потоков - это глобальное, а не локальное свойство программы. Причина, по которой так трудно получить права, заключается в том, что вы должны полностью знать поведение потоков всей программы, чтобы обеспечить ее безопасность.
Опять же, посмотрите на мой пример: каждый метод тривиален. Именно способ, которым методы взаимодействуют друг с другом на "глобальном" уровне, делает программу тупиковой. Вы не можете смотреть на каждый метод и отмечать его как "безопасный", а затем ожидать, что вся программа безопасна, даже если вы не можете сделать вывод, что, поскольку ваш дом сделан из 100% -го пустотелого кирпича, этот дом также не полый. Пустота дома является глобальной собственностью всего объекта, а не совокупностью свойств его частей.
Там нет жесткого и быстрого правила.
Вот несколько правил, чтобы сделать потокобезопасным код в.NET, и почему это не очень хорошие правила:
- Функция и все функции, которые она вызывает, должны быть чистыми (без побочных эффектов) и использовать локальные переменные. Хотя это сделает ваш код поточно-ориентированным, очень мало интересных вещей, которые вы можете сделать с этим ограничением в.NET.
- Каждая функция, которая работает с общим объектом, должна
lock
на общее дело. Все замки должны быть выполнены в том же порядке. Это сделает поток кода безопасным, но это будет невероятно медленно, и вы можете также не использовать несколько потоков. - ...
Не существует правила, которое делает поток кода безопасным, единственное, что вы можете сделать, это убедиться, что ваш код будет работать независимо от того, сколько раз он активно выполняется, каждый поток может быть прерван в любой момент, причем каждый поток находится в свое собственное состояние / местоположение, и это для каждой функции (статической или другой), которая обращается к общим объектам.
Он должен быть синхронизирован с использованием блокировки объекта, без сохранения состояния или неизменяемым.
ссылка: http://docs.oracle.com/javase/tutorial/essential/concurrency/immutable.html