Что означает потокобезопасность? - PullRequest
106 голосов
/ 09 января 2010

Недавно я попытался получить доступ к текстовому полю из потока (кроме потока пользовательского интерфейса), и возникло исключение. В нем говорилось что-то о «коде, не поддерживающем многопоточность», поэтому я закончил тем, что написал делегат (пример из MSDN помог) и вызвал его вместо этого.

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

Обновление: Буду ли я сталкиваться с серьезными проблемами, если я проверю

Controls.CheckForIllegalCrossThread..blah =true

Ответы [ 11 ]

109 голосов
/ 09 января 2010

Эрик Липперт имеет хороший пост в блоге, озаглавленный Что это за вещь, которую вы называете «потокобезопасный»? об определении безопасности потоков, найденном в Википедии.

3 важных вещи, извлеченных из ссылок:

«Часть кода является поточно-ориентированной, если она работает правильно во время одновременное выполнение несколькими потоками. ”

«В частности, он должен удовлетворять потребность в нескольких потоках для получить доступ к одним и тем же общим данным… »

«… и необходимость доступа к общему фрагменту данных только одному нить в любое время. ”

Определенно стоит прочитать!

92 голосов
/ 09 января 2010

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

16 голосов
/ 09 января 2010

В Википедии есть статья о безопасности потоков.

Эта страница определений (вы должны пропустить объявление - извините) определяет его следующим образом:

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

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

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

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

  • Работа с копиями данных
  • Добавление блокировок вокруг критического кода
5 голосов
/ 22 октября 2013

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

Рассмотрим следующий метод:

private int myInt = 0;
public int AddOne()
{
    int tmp = myInt;
    tmp = tmp + 1;
    myInt = tmp;
    return tmp;
}

Теперь поток A и поток B оба хотели бы выполнить AddOne (). но A запускается первым и считывает значение myInt (0) в tmp. Теперь по какой-то причине планировщик решает остановить поток A и отложить выполнение до потока B. Теперь поток B также считывает значение myInt (все еще 0) в свою собственную переменную tmp. Поток B завершает весь метод, поэтому в конце myInt = 1. И 1 возвращается. Теперь снова очередь за темой. Тема А продолжается. И добавляет 1 к tmp (tmp был 0 для потока A). И затем сохраняет это значение в myInt. myInt снова 1.

Таким образом, в этом случае метод AddOne вызывался два раза, но поскольку метод не был реализован в поточно-ориентированном способе, значение myInt не равно 2, как ожидалось, а 1, поскольку второй поток считал переменную myInt перед Первый поток завершил обновление.

Создание потоково-безопасных методов очень сложно в нетривиальных случаях. И есть немало методов. В Java вы можете пометить метод как синхронизированный, это означает, что только один поток может выполнить этот метод в данный момент времени. Другие потоки ждут своей очереди. Это делает метод потокобезопасным, но если в методе нужно выполнить много работы, то это приведет к напрасной трате пространства. Другой метод - 'пометить только небольшую часть метода как синхронизированную' , создав блокировку или семафор и заблокировав эту небольшую часть (обычно называемую критической секцией). Есть даже некоторые методы, которые реализованы как поточно-безопасные без блокировки, что означает, что они построены таким образом, что несколько потоков могут проходить через них одновременно, не вызывая проблем, это может быть в случае, когда метод только выполняет один атомарный вызов. Атомарные вызовы - это вызовы, которые не могут быть прерваны и могут выполняться только одним потоком за раз.

4 голосов
/ 07 июля 2017

В реальном мире примером для неспециалистов является

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

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

4 голосов
/ 15 июля 2014

Вы можете получить более подробное объяснение из книги «Параллелизм Java на практике»:

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

4 голосов
/ 09 января 2010

Модуль является поточно-ориентированным, если он гарантирует, что может поддерживать свои инварианты при многопоточном и параллельном использовании.

Здесь модуль может быть структурой данных, классом, объектом, методом / процедурой или функцией. Кусок кода и связанных с ним данных.

Гарантия потенциально может быть ограничена определенными средами, такими как конкретная архитектура ЦП, но должна действовать для этих сред. Если явного разграничения сред нет, то обычно подразумевается, что для всех сред можно скомпилировать и выполнить код.

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

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

3 голосов
/ 20 мая 2016

Потоковая безопасность : Поточно-ориентированная программа защищает свои данные от ошибок согласованности памяти. В сильно многопоточной программе потокобезопасная программа не вызывает побочных эффектов при множественных операциях чтения / записи из нескольких потоков на одних и тех же объектах. Различные потоки могут совместно использовать и изменять данные объекта без ошибок согласованности.

Вы можете достичь безопасности потоков, используя расширенный API параллелизма. Эта документация page содержит хорошие программные конструкции для обеспечения безопасности потоков.

Блокировка объектов поддерживает блокировку идиом, которые упрощают многие параллельные приложения.

Исполнители определяют высокоуровневый API для запуска и управления потоками. Реализации исполнителя, предоставляемые java.util.concurrent, обеспечивают управление пулом потоков, подходящее для крупномасштабных приложений.

Параллельные коллекции упрощают управление большими коллекциями данных и могут значительно сократить потребность в синхронизации.

Атомные переменные имеют функции, которые минимизируют синхронизацию и помогают избежать ошибок согласованности памяти.

ThreadLocalRandom (в JDK 7) обеспечивает эффективную генерацию псевдослучайных чисел из нескольких потоков.

См. Также java.util.concurrent и java.util.concurrent.atomic для других программных конструкций.

1 голос
/ 01 ноября 2011

Я нахожу концепцию http://en.wikipedia.org/wiki/Reentrancy_%28computing%29 такой, которую я обычно считаю небезопасной многопоточностью, когда метод имеет побочный эффект, такой как глобальная переменная, и полагается на него.

Например, я видел код, который форматировал числа с плавающей запятой в строку, если два из них выполняются в разных потоках, глобальное значение decimalSeparator может быть постоянно изменено на '.'

//built in global set to locale specific value (here a comma)
decimalSeparator = ','

function FormatDot(value : real):
    //save the current decimal character
    temp = decimalSeparator

    //set the global value to be 
    decimalSeparator = '.'

    //format() uses decimalSeparator behind the scenes
    result = format(value)

    //Put the original value back
    decimalSeparator = temp
1 голос
/ 09 января 2010

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

Обычная практика WinForms - это создание единого потока, предназначенного для всей вашей работы с пользовательским интерфейсом.

...