Что такое конкуренция потоков?

121

Может кто-нибудь, пожалуйста, просто объясните, что такое разногласия?

Я погуглил, но не могу найти простого объяснения.

Тони Лев
источник
10
Так что запишите, что вы об этом думаете, чтобы мы могли увидеть, где вы находитесь, или ваше понимание может быть правильным.
Джеймс Блэк,

Ответы:

89

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

keyboardP
источник
53
Этот ответ неполный (как и большинство других). Хотя блокировка - это одна из причин, по которой могут возникать споры, это далеко не единственная такая вещь. Также может возникнуть конкуренция за ресурсы без блокировки. (Например, если два потока продолжают атомарно увеличивать одно и то же целое число, они могут столкнуться с конфликтом из-за пинг-понга кеша. Блокировки не используются.)
Дэвид Шварц
В случае глобальной блокировки интерпретатора (GIL), такой как в CPython, где поток всегда должен получать GIL, несколько потоков, выполняющихся в одном процессе, по умолчанию конкурируют.
Acumenus
Я думаю, вы объяснили это с точки зрения тупика, но он сильно отличается от тупика.
Харшит Гупта
185

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

Самый очевидный пример конфликта - блокировка. Если поток A имеет блокировку, а поток B хочет получить ту же самую блокировку, поток B должен будет ждать, пока поток A не освободит блокировку.

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

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

Зачем? Скажем, каждый поток работает на своем собственном ядре на современном процессоре x86, а ядра не используют общий кеш L2. С помощью всего одного потока объект может оставаться в кэше L2 большую часть времени. Когда оба потока работают, каждый раз, когда один поток изменяет объект, другой поток обнаруживает, что данных нет в его кэше L2, потому что другой ЦП аннулировал строку кэша. Например, на Pentium D это приведет к тому, что код будет работать со скоростью FSB, которая намного меньше скорости кэш-памяти L2.

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

Как ни странно, блокировки обычно уменьшают конкуренцию. Зачем? Поскольку без блокировки два потока могут работать с одним и тем же объектом или коллекцией и вызывать много конфликтов (например, есть очереди без блокировки). Блокировки будут стремиться исключить из расписания конкурирующие потоки, позволяя вместо этого запускать неконкурирующие потоки. Если поток A содержит блокировку, а поток B хочет такую ​​же блокировку, реализация может вместо этого запустить поток C. Если потоку C не нужна эта блокировка, то будущего конфликта между потоками A и B можно избежать на некоторое время. (Конечно, это предполагает, что есть другие потоки, которые могут выполняться. Это не поможет, если единственный способ, которым система в целом может добиться полезного прогресса, - это запуск потоков, которые конкурируют.)

Дэвид Шварц
источник
4
+1 Кроме того, чтобы сделать это явным, две переменные, за которые борются два ядра, даже не обязательно должны быть одной и той же переменной, чтобы вызвать конкуренцию, они должны только храниться в памяти в одной строке кеша. Заполнение структур и / или выравнивание структур по памяти может помочь избежать этой формы конфликта.
Rob_before_edits
1
@David, пожалуйста, помогите разобраться в последнем абзаце вашего ответа более подробно
Ученик
4
@Naroji Задайте вопрос по этому поводу.
Дэвид Шварц
@DavidSchwartz, вы программист на C?
Pacerier 08
@Pacerier C ++ в основном.
Дэвид Шварц
19

От сюда :

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

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

Джон Б
источник
Это также объясняет разницу между
конфликтом
3

Я думаю, что должны быть некоторые разъяснения от OP на фоне вопроса - я могу придумать 2 ответа (хотя я уверен, что в этот список есть дополнения):

  1. если вы имеете в виду общую «концепцию» конкуренции потоков и то, как она может проявляться в приложении, я полагаюсь на подробный ответ @ DavidSchwartz выше.

  2. Существует также счетчик производительности .NET CLR Locks and Threads: Total # of Contentions. Согласно описанию PerfMon для этого счетчика, он определяется как:

    Этот счетчик отображает общее количество раз, когда потоки в CLR безуспешно пытались получить управляемую блокировку. Управляемые блокировки можно получить разными способами; с помощью оператора «lock» в C # или путем вызова System.Monitor.Enter или с помощью настраиваемого атрибута MethodImplOptions.Synchronized.

... и я уверен, что другие для других ОС и приложений.

Дэйв Блэк
источник
2

У вас 2 потока. Thread A и Thread B, у вас также есть объект C.

A в настоящее время обращается к объекту C и заблокировал этот объект. B должен получить доступ к объекту C, но не может этого сделать, пока A не снимет блокировку с объекта C.

Аарон М
источник
1

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

Марк Уилкинс
источник
1

Для меня состязание - это соревнование между 2 или более потоками за общий ресурс. Ресурс может быть замком, счетчиком и т. Д. Конкуренция означает «кто первым получит». Чем больше нитей, тем больше разногласий. Чем чаще доступ к ресурсу, тем выше конкуренция.

Маца
источник
1

Представьте себе следующий сценарий. Вы готовитесь к завтрашнему заключительному экзамену и немного проголодались. Итак, вы даете своему младшему брату десять баксов и просите его купить вам пиццу. В этом случае вы являетесь основным потоком, а ваш брат - дочерним потоком. После того, как ваш заказ сделан, вы и ваш брат одновременно выполняете свою работу (т. Е. Учитесь и покупаете пиццу). Теперь нам нужно рассмотреть два случая. Сначала ваш брат приносит вашу пиццу и прекращает занятия, пока вы учитесь. В этом случае можно бросить учебу и насладиться пиццей. Во-вторых, вы рано заканчиваете учебу и ложитесь спать (то есть, ваша назначенная работа на сегодня - подготовка к завтрашнему выпускному экзамену - выполнена) до того, как пицца будет доступна. Конечно, вы не можете заснуть; иначе у вас не будет возможности съесть пиццу.

Как и в примере, эти два случая означают соперничество.

ОСШ
источник
0

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

amilamad
источник
0

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

из документации dotTrace

AndreyT
источник