Я слышал эти слова, связанные с параллельным программированием, но в чем разница между ними?
Я слышал эти слова, связанные с параллельным программированием, но в чем разница между ними?
Ответы:
Блокировка позволяет только одному потоку войти в заблокированную часть, и блокировка не используется другими процессами.
Мьютекс - это то же самое, что и блокировка, но он может быть системным (общим для нескольких процессов).
Семафора делает то же самое , как взаимная блокировку , но позволяет й количество потоков , чтобы войти, это может быть использовано, например , чтобы ограничить число процессоров, Ио~d или баран ресурсоемких задач , выполняющиеся в то же время.
Более подробный пост о различиях между мьютексом и семафором читайте здесь .
У вас также есть блокировки чтения / записи, которые позволяют неограниченное количество читателей или 1 писатель в любой момент времени.
Есть много заблуждений относительно этих слов.
Это из предыдущего поста ( https://stackoverflow.com/a/24582076/3163691 ), который отлично подходит здесь:
1) Критический раздел = Пользовательский объект, используемый для разрешения выполнения только одного активного потока из множества других в одном процессе . Другие не выбранные потоки (@ получение этого объекта) помещаются в спящий режим .
Нет возможности межпроцессного, очень примитивный объект.
2) Семафор Mutex (также известный как Mutex) = объект ядра, используемый для разрешения выполнения только одного активного потока из множества других, среди различных процессов . Другие не выбранные потоки (@ получение этого объекта) помещаются в спящий режим . Этот объект поддерживает владение потоком, уведомление о прекращении потока, рекурсию (множественные вызовы «получения» из одного потока) и «предотвращение инверсии приоритетов».
Межпроцессная возможность, очень безопасная в использовании, своего рода объект синхронизации «высокого уровня».
3) Подсчет семафора (он же семафор) = объект ядра, используемый для разрешения выполнения группы активных потоков из множества других. Другие не выбранные потоки (@ получение этого объекта) помещаются в спящий режим .
Однако возможность межпроцессного использования не очень безопасна, поскольку в ней отсутствуют следующие атрибуты 'mutex': уведомление о завершении потока, рекурсия ?, 'предотвращение инверсии приоритета'? И т. Д.].
4) А теперь, говоря о спин-замках, сначала несколько определений:
Критическая область = область памяти, совместно используемая двумя или более процессами.
Lock = Переменная, значение которой разрешает или запрещает вход в «критическую область». (Это может быть реализовано как простой «логический флаг»).
Ожидание при занятости = постоянное тестирование переменной до появления какого-либо значения.
В заключение:
Spin-lock (также известный как Spinlock) = блокировка, которая использует ожидание занятым . (Получение блокировки производится с помощью xchg или аналогичных атомарных операций ).
[Нет спящего потока, в основном используется только на уровне ядра. Неэффективный для кода уровня пользователя.
В качестве последнего комментария я не уверен, но могу поспорить с вами, что большие первые синхронизирующие объекты (# 1, # 2 и # 3), указанные выше, используют этого простого зверя (# 4) как часть своей реализации.
Хорошего дня!.
Ссылки:
-Концепции реального времени для встраиваемых систем Цин Ли с Кэролайн Яо (CMP Books).
Современные операционные системы (3-е место) Эндрю Таненбаума (Pearson Education International).
-Программирование приложений для Microsoft Windows (4-е) от Джеффри Рихтера (Microsoft Programming Series).
Кроме того, вы можете взглянуть на: https://stackoverflow.com/a/24586803/3163691
Большинство проблем можно решить с помощью (i) просто блокировок, (ii) просто семафоров, ... или (iii) комбинации обоих! Как вы, возможно, обнаружили, они очень похожи: оба препятствуют состязаниям , оба имеют acquire()
/ release()
операции, оба приводят к блокировке / подозрению на ноль или более потоков ... Действительно, принципиальное различие заключается исключительно в том, как они блокируют и разблокируют .
Для обеих блокировок / семафоров попытка вызова, acquire()
когда примитив находится в состоянии 0, приводит к приостановке вызывающего потока. Для блокировок - попытки получить блокировку в состоянии 1 успешны. Для семафоров - попытки получить блокировку в состояниях {1, 2, 3, ...} успешны.
Для блокировок в состоянии 0, если тот же поток, который ранее вызывал acquire()
, теперь вызывает release, то релиз успешен. Если другой поток попробовал это - дело за реализацией / библиотекой относительно того, что происходит (обычно попытка игнорируется или выдается ошибка). Для семафоров в состоянии 0 любой поток может вызвать release, и он будет успешным (независимо от того, какой поток использовался ранее, чтобы перевести семафор в состояние 0).
Из предыдущего обсуждения мы видим, что у замков есть понятие владельца (единственный поток, который может вызвать release, является владельцем), в то время как у семафоров нет владельца (любой поток может вызывать release на семафоре).
Что вызывает много путаницы, так это то, что на практике они представляют собой множество вариаций этого определения высокого уровня.
Важные варианты для рассмотрения :
acquire()
/ release()
называться? - [Различная массово ]Это зависит от вашей книги / лектора / языка / библиотеки / окружения.
Вот краткий обзор того, как некоторые языки отвечают на эти детали.
pthread_mutex_t
. По умолчанию они не могут использоваться совместно с другими процессами ( PTHREAD_PROCESS_PRIVATE
), однако мьютексы имеют атрибут pshared . Если установлено, то мьютекс является общим для процессов ( PTHREAD_PROCESS_SHARED
).sem_t
. Подобно мьютексам, семафоры могут быть разделены между потоками многих процессов или могут быть приватными для потоков одного отдельного процесса. Это зависит от аргумента pshared, предоставленного sem_init
.threading.RLock
) в основном такой же , как C / C ++ pthread_mutex_t
с. Оба являются реентерабельными . Это означает, что они могут быть разблокированы только тем потоком, который его заблокировал. Это тот случай, когда sem_t
семафоры, threading.Semaphore
семафоры и theading.Lock
блокировки не являются реентерабельными - это тот случай, когда любой поток может выполнить разблокировку / блокировку семафора.threading.Semaphore
) в основном такой же , как sem_t
. Хотя с sem_t
помощью, очередь идентификаторов потоков используется для запоминания порядка, в котором потоки блокировались при попытке заблокировать его, пока он заблокирован. Когда поток разблокирует семафор, в качестве нового владельца выбирается первый поток в очереди (если он есть). Идентификатор потока удаляется из очереди, и семафор снова блокируется. Однако с threading.Semaphore
помощью набора используется вместо очереди, поэтому порядок, в котором потоки блокировались, не сохраняется - любой поток в наборе может быть выбран следующим владельцем.java.util.concurrent.ReentrantLock
) в основном так же , как C / C ++ pthread_mutex_t
«s и Питон threading.RLock
в том , что она также реализует возвратный замок. Совместное использование блокировок между процессами в Java сложнее, поскольку JVM выступает в качестве посредника. Если поток пытается разблокировать блокировку, которой он не владеет, создается IllegalMonitorStateException
исключение.java.util.concurrent.Semaphore
), в основном такой же , как sem_t
и threading.Semaphore
. Конструктор для семафоров Java принимает логический параметр fairness, который управляет использованием набора (false) или очереди (true) для хранения ожидающих потоков. Теоретически семафоры часто обсуждаются, но на практике семафоры используются не так часто. Семафор содержит состояние только одного целого числа, поэтому часто он довольно негибкий, и многие нужны сразу, что вызывает трудности в понимании кода. Кроме того, тот факт, что любой поток может выпустить семафор, иногда нежелателен. Вместо этого используются более объектно-ориентированные / высокоуровневые примитивы / абстракции синхронизации, такие как «переменные условия» и «мониторы».
Взгляните на учебник по многопоточности Джона Копплина.
В разделе « Синхронизация между потоками» он объясняет различия между событием, блокировкой, мьютексом, семафором, ожидаемым таймером.
Мьютекс может принадлежать только один поток в то время, позволяя темы координировать взаимно исключающий доступ к общему ресурсу
Объекты критического раздела обеспечивают синхронизацию, аналогичную объектам мьютекса, за исключением того, что объекты критического раздела могут использоваться только потоками одного процесса
Другое различие между мьютексом и критической секцией состоит в том, что, если объект критической секции в настоящее время принадлежит другому потоку, он
EnterCriticalSection()
ожидает неопределенное время владения, тогда какWaitForSingleObject()
, который используется с мьютексом, позволяет вам указать тайм-аутСемафора поддерживает подсчет между нулем и некоторым максимальным значением, ограничивая количество потоков, которые одновременно доступ к совместно используемому ресурсу.
Я постараюсь покрыть это примерами:
Блокировка. Одним из примеров, в котором вы будете использовать, lock
будет общий словарь, в который добавляются элементы (которые должны иметь уникальные ключи).
Блокировка гарантирует, что один поток не войдет в механизм кода, который проверяет наличие элемента в словаре, в то время как другой поток (который находится в критическом разделе) уже прошел эту проверку и добавляет элемент. Если другой поток пытается ввести заблокированный код, он будет ждать (будет заблокирован), пока объект не будет освобожден.
private static readonly Object obj = new Object();
lock (obj) //after object is locked no thread can come in and insert item into dictionary on a different thread right before other thread passed the check...
{
if (!sharedDict.ContainsKey(key))
{
sharedDict.Add(item);
}
}
Семафор: Допустим, у вас есть пул соединений, тогда один поток может зарезервировать один элемент в пуле, ожидая, пока семафор установит соединение. Затем он использует соединение, а когда работа завершена, освобождает соединение, освобождая семафор.
Пример кода, который я люблю, - это пример баунсера, который дал @Patric .
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace TheNightclub
{
public class Program
{
public static Semaphore Bouncer { get; set; }
public static void Main(string[] args)
{
// Create the semaphore with 3 slots, where 3 are available.
Bouncer = new Semaphore(3, 3);
// Open the nightclub.
OpenNightclub();
}
public static void OpenNightclub()
{
for (int i = 1; i <= 50; i++)
{
// Let each guest enter on an own thread.
Thread thread = new Thread(new ParameterizedThreadStart(Guest));
thread.Start(i);
}
}
public static void Guest(object args)
{
// Wait to enter the nightclub (a semaphore to be released).
Console.WriteLine("Guest {0} is waiting to entering nightclub.", args);
Bouncer.WaitOne();
// Do some dancing.
Console.WriteLine("Guest {0} is doing some dancing.", args);
Thread.Sleep(500);
// Let one guest out (release one semaphore).
Console.WriteLine("Guest {0} is leaving the nightclub.", args);
Bouncer.Release(1);
}
}
}
Мьютекс В значительной степени Semaphore(1,1)
и часто используется во всем мире (в других случаях применение lock
более целесообразно). Можно использовать global Mutex
при удалении узла из глобально доступного списка (последнее, что вы хотите, чтобы другой поток делал что-то, пока вы удаляете узел). Когда вы получите, Mutex
если другой поток попытается получить одно и Mutex
то же, он будет переведен в спящий режим, пока тот же поток, который получил его, не Mutex
освободит его.
Хороший пример создания глобального мьютекса - @deepee
class SingleGlobalInstance : IDisposable
{
public bool hasHandle = false;
Mutex mutex;
private void InitMutex()
{
string appGuid = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), false).GetValue(0)).Value.ToString();
string mutexId = string.Format("Global\\{{{0}}}", appGuid);
mutex = new Mutex(false, mutexId);
var allowEveryoneRule = new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), MutexRights.FullControl, AccessControlType.Allow);
var securitySettings = new MutexSecurity();
securitySettings.AddAccessRule(allowEveryoneRule);
mutex.SetAccessControl(securitySettings);
}
public SingleGlobalInstance(int timeOut)
{
InitMutex();
try
{
if(timeOut < 0)
hasHandle = mutex.WaitOne(Timeout.Infinite, false);
else
hasHandle = mutex.WaitOne(timeOut, false);
if (hasHandle == false)
throw new TimeoutException("Timeout waiting for exclusive access on SingleInstance");
}
catch (AbandonedMutexException)
{
hasHandle = true;
}
}
public void Dispose()
{
if (mutex != null)
{
if (hasHandle)
mutex.ReleaseMutex();
mutex.Dispose();
}
}
}
тогда используйте как:
using (new SingleGlobalInstance(1000)) //1000ms timeout on global lock
{
//Only 1 of these runs at a time
GlobalNodeList.Remove(node)
}
Надеюсь, это сэкономит вам время.
В Википедии есть отличный раздел о различиях между семафорами и мьютексами :
Мьютекс - это то же самое, что и двоичный семафор, и иногда он использует ту же базовую реализацию. Различия между ними:
У мьютексов есть понятие владельца, то есть процесс, который заблокировал мьютекс. Только процесс, который заблокировал мьютекс, может разблокировать его. В отличие от семафора не имеет понятия владельца. Любой процесс может разблокировать семафор.
В отличие от семафоров, мьютексы обеспечивают безопасность инверсии приоритетов. Поскольку мьютекс знает своего текущего владельца, можно повысить приоритет владельца, когда на мьютексе начинает ожидаться задача с более высоким приоритетом.
Мьютексы также обеспечивают безопасность удаления, когда процесс, содержащий мьютекс, не может быть случайно удален. Семафоры не предоставляют этого.
Насколько я понимаю, мьютекс предназначен только для использования в одном процессе, но во многих его потоках, тогда как семафор может использоваться в нескольких процессах и в соответствующих наборах потоков.
Кроме того, мьютекс является двоичным (он либо заблокирован, либо разблокирован), тогда как семафор имеет понятие подсчета или очередь из нескольких запросов на блокировку и разблокировку.
Может ли кто-нибудь проверить мое объяснение? Я говорю в контексте Linux, в частности Red Hat Enterprise Linux (RHEL) версии 6, которая использует ядро 2.6.32.
Использование программирования на C для варианта Linux в качестве базового примера.
Замок:
• Обычно очень простой двоичный файл конструкции, либо заблокированный, либо разблокированный.
• Нет концепции владения потоком, приоритета, последовательности и т. Д.
• Обычно спин-блокировка, когда поток постоянно проверяет наличие блокировок.
• Обычно полагается на атомарные операции, например, «Тестировать и устанавливать», сравнивать и менять, извлекать и добавлять и т. Д.
• Обычно требуется аппаратная поддержка для атомарной работы.
Файловые блокировки:
• Обычно используется для координации доступа к файлу через несколько процессов.
• Несколько процессов могут удерживать блокировку чтения, однако, когда любой отдельный процесс удерживает блокировку записи, никакому другому процессу не разрешается получать блокировку чтения или записи.
• Пример: flock, fcntl и т. Д.
мьютекс:
• Вызовы функции Mutex обычно работают в пространстве ядра и приводят к системным вызовам.
• Используется концепция собственности. Только поток, который в настоящее время содержит мьютекс, может разблокировать его.
• Мьютекс не является рекурсивным (Исключение: PTHREAD_MUTEX_RECURSIVE).
• Обычно используется в ассоциации с переменными условия и передается в качестве аргументов, например, pthread_cond_signal, pthread_cond_wait и т. Д.
• В некоторых системах UNIX мьютекс может использоваться несколькими процессами, хотя это может быть не реализовано во всех системах.
Семафор:
• Это целое число, поддерживаемое ядром, значения которого не могут опускаться ниже нуля.
• Может использоваться для синхронизации процессов.
• Значение семафора может быть установлено на значение больше 1, и в этом случае значение обычно указывает количество доступных ресурсов.
• Семафор, значение которого ограничено 1 и 0, называется двоичным семафором.
Supporting ownership
, maximum number of processes share lock
А также maximum number of allowed processes/threads in critical section
три основных фактора , которые определяют имя / тип параллельного объекта с общим названием lock
. Поскольку значения этих факторов являются двоичными (имеют два состояния), мы можем суммировать их в 3 * 8 правдоподобной таблице.
X Y Z Name
--- --- --- ------------------------
0 ∞ ∞ Semaphore
0 ∞ 1 Binary Semaphore
0 1 ∞ SemaphoreSlim
0 1 1 Binary SemaphoreSlim(?)
1 ∞ ∞ Recursive-Mutex(?)
1 ∞ 1 Mutex
1 1 ∞ N/A(?)
1 1 1 Lock/Monitor
Не стесняйтесь редактировать или расширять эту таблицу, я разместил ее как таблицу ascii, чтобы ее можно было редактировать :)