Я чув ці слова, пов’язані з одночасним програмуванням, але в чому різниця між ними?
Я чув ці слова, пов’язані з одночасним програмуванням, але в чому різниця між ними?
Відповіді:
Блокування дозволяє лише одному потоку входити в заблоковану частину, а блокування не ділиться жодними іншими процесами.
Мютекс такий же, як і замок, але він може бути загальносистемним (поділяється кількома процесами).
Семафора робить те ж саме , як взаємна блокування , але дозволяє й кількість потоків , щоб увійти, це може бути використано, наприклад , щоб обмежити число процесорів, Іо ~ d або баран ресурсномістких завдань , що виконуються в той же час.
Більш докладний пост про відмінності мютексу та семафору читайте тут .
Ви також маєте блоки читання / запису, що дозволяє або необмежену кількість читачів, або 1 письменника в будь-який момент часу.
Щодо цих слів багато помилок.
Це з попереднього допису ( https://stackoverflow.com/a/24582076/3163691 ), який тут чудово підходить:
1) Критичний розділ = Об'єкт користувача, який використовується для дозволу виконання лише одного активного потоку з багатьох інших в межах одного процесу . Інші не вибрані нитки (@ придбання цього об'єкта) переходять у режим сну .
[Ніякої можливості міжпроцесорної роботи, дуже примітивний об’єкт].
2) Mutex Semaphore (він же Mutex) = об'єкт ядра, який використовується для дозволу виконання лише одного активного потоку з багатьох інших, в різних процесах . Інші не вибрані нитки (@ придбання цього об'єкта) переходять у режим сну . Цей об’єкт підтримує право власності на потоки, сповіщення про припинення потоку, рекурсію (декілька викликів «придбання» з одного потоку) та «уникнення пріоритетної інверсії».
[Можливість міжпроцесорної роботи, дуже безпечна у використанні, свого роду об'єкт синхронізації високого рівня].
3) Підрахунок Semaphore (він же Semaphore) = об'єкт Kernel, який використовується для дозволу виконання групи активних потоків з багатьох інших. Інші не вибрані нитки (@ придбання цього об'єкта) переходять у режим сну .
[Можливість міжпроцесорної роботи, однак не дуже безпечна для використання, оскільки у неї відсутні такі атрибути "mutex": повідомлення про припинення потоку, рекурсія ?, "уникнення пріоритетної інверсії" тощо тощо.
4) А тепер, говорячи про "спінкі", спочатку кілька визначень:
Критична область = область пам’яті, поділеної двома або більше процесами.
Lock = Змінна, значення якої дозволяє або забороняє вхід до 'критичної області'. (Це може бути реалізовано у вигляді простого "булевого прапора").
Зайняте очікування = Безперервне тестування змінної, поки не з’явиться якесь значення.
Нарешті:
Spin-lock (він же Spinlock) = Замок, який використовує зайняте очікування . ( Придбання блокування здійснюється за допомогою xchg або подібних атомних операцій ).
[Немає потокових сну, в основному використовується лише на рівні ядра. Неіснуючий код рівня користувача].
Як останнє зауваження, я не впевнений, але я можу поставити під сумнів кілька великих доларів, що вищезгадані перші 3 об’єкти синхронізації (№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()
, тепер викликає випуск, то випуск успішний. Якщо інший потік спробував це - він залежить від реалізації / бібліотеки щодо того, що відбувається (зазвичай спроба ігнорується або викидається помилка). Для семафорів у стані 0 будь-яка нитка може викликати вивільнення, і вона буде успішною (незалежно від того, який потік використовувався попередньо, щоб перевести семафор у стан 0).
З попереднього обговорення ми бачимо, що у замках є поняття власника (єдиною ниткою, яка може викликати звільнення, є власник), тоді як семафори не мають власника (будь-яка нитка може викликати звільнення на семафорі).
Що викликає велику плутанину, це те, що на практиці це багато варіантів цього визначення високого рівня.
Важливі варіанти, які слід врахувати :
acquire()
/ release()
називатися? - [Варіюється масово ]Це залежить від вашої книги / лектора / мови / бібліотеки / середовища.
Ось короткий тур, зазначаючи, як деякі мови відповідають на ці деталі.
pthread_mutex_t
. За замовчуванням вони не можуть бути надані спільним з будь-якими іншими процесами ( PTHREAD_PROCESS_PRIVATE
), однак мутекс має атрибут під назвою pshared . Якщо встановлено, то мютекс поділяється між процесами ( PTHREAD_PROCESS_SHARED
).sem_t
. Подібно до мутексів, семафори можуть поділятися між трійками багатьох процесів або залишатись приватними нитками одного єдиного процесу. Це залежить від наданого аргументу PSharedsem_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 приймає булевий параметр справедливості, який керує тим, чи використовувати набір (false) чи чергу (true) для зберігання потоків очікування. Теоретично семафори часто обговорюються, але на практиці семафори використовуються не так сильно. Семафор тримає лише одне ціле число, тому часто він є досить негнучким, і багато з них потрібні відразу - спричиняючи труднощі в розумінні коду. Крім того, той факт, що будь-яка нитка може випустити семафор, іноді небажаний. Натомість використовуються більше об'єктно-орієнтовані / примітивні / абстракції синхронізації вищого рівня, такі як "змінні умови" та "монітори".
Погляньте на багатопоточний підручник Джона Коппліна.
У розділі Синхронізація між нитками він пояснює відмінності між подією, замком, мютекс, семафором, очікуваним таймером
М'ютекс може належати тільки один потік в той час, дозволяючи теми координувати взаємно виключає доступ до загального ресурсу
Об'єкти критичного розділу забезпечують синхронізацію, аналогічну тій, що надається mutex-об'єктами, за винятком того, що об'єкти критичного розділу можуть використовуватися лише потоками одного процесу
Ще одна відмінність між mutex і критичним розділом полягає в тому, що якщо об'єкт критичного розділу зараз належить іншому потоку, він
EnterCriticalSection()
чекає нескінченно право власності, тоді якWaitForSingleObject()
, який використовується з mutex, дозволяє вказати час очікуванняСемафора підтримує підрахунок між нулем і деяким максимальним значенням, обмежуючи кількість потоків, які одночасно доступ до спільно використовуваному ресурсу.
Я спробую розкрити це на прикладах:
Блокування. Одним із прикладів використання 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);
}
}
}
Mutex Це дуже багато Semaphore(1,1)
і часто використовується в усьому світі (широке застосування, інакше, можливо lock
, доцільніше). Можна було б використовувати global Mutex
під час видалення вузла із глобально доступного списку (останнє, що ви хочете, щоб інший потік щось робив під час видалення вузла). Коли ви придбаєте, Mutex
якщо інша нитка намагається придбати однакову, Mutex
вона буде переведена в режим сну до тих пір, коли SAME, що придбав її, не 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)
}
Сподіваюсь, це заощадить певний час.
У Вікіпедії є великий розділ про відмінності між семафорами та мутексами :
Мутекс - це по суті те саме, що і двійковий семафор, і іноді використовує ту саму основну реалізацію. Відмінності між ними:
Mutexes має поняття власника, який є процесом, який замикав мютекс. Тільки процес, який заблокував мютекс, може розблокувати його. На противагу цьому, семафор не має поняття власника. Будь-який процес може розблокувати семафор.
На відміну від семафорів, мютекси забезпечують пріоритетну безпеку інверсії. Оскільки мютекс знає свого поточного власника, можливо підвищити пріоритет власника кожного разу, коли на мутекс почнеться очікування вищого пріоритету.
Mutexes також забезпечує безпеку при видаленні, коли процес, що містить мутекс, не може бути випадково видалений. Семафори цього не забезпечують.
Я розумію, що мютекс використовується лише в рамках одного процесу, але в багатьох його потоках, тоді як семафор може використовуватися в декількох процесах і в їх відповідних наборах потоків.
Також мютекс є двійковим (він заблокований або розблокований), тоді як семафор має поняття підрахунку, або чергу з декількох запитів блокування та розблокування.
Чи може хтось підтвердити моє пояснення? Я говорю в контексті Linux, зокрема Red Hat Enterprise Linux (RHEL) версії 6, яка використовує ядро 2.6.32.
Використання програмування на C у варіанті Linux в якості базового випадку для прикладів.
Блокування:
• Зазвичай дуже простий бінарний конструктор в роботі або заблокований, або розблокований
• Немає концепції власності на потоки, пріоритетності, послідовності тощо.
• Зазвичай спіновий замок, де нитка постійно перевіряє наявність замків.
• Зазвичай покладається на атомні операції, наприклад, тестування та встановлення, порівняння та заміна, отримання та додавання тощо.
• Зазвичай потрібна апаратна підтримка для атомної роботи.
Блокування файлів:
• Зазвичай використовується для координації доступу до файлу через декілька процесів.
• Декілька процесів можуть утримувати блокування читання, але коли будь-який процес містить блокування запису, жоден інший процес не має права придбати блокування читання або запису.
• Приклад: flock, fcntl тощо.
Mutex:
• Виклики функції Mutex зазвичай працюють у просторі ядра та призводять до системних викликів.
• Він використовує поняття власності. Тільки нитка, в якій зараз міститься мютекс, може розблокувати її.
• Mutex не є рекурсивним (виняток: PTHREAD_MUTEX_RECURSIVE).
• Зазвичай використовується в асоціації зі змінними стану і передається як аргументи, наприклад, pthread_cond_signal, pthread_cond_wait і т.д.
• Деякі системи UNIX дозволяють mutex використовувати декілька процесів, хоча це може бути застосовано не у всіх системах.
Семафор:
• Це ціле число, яке підтримує ядро, значення якого не дозволяють опускатися нижче нуля.
• Він може використовуватися для синхронізації процесів.
• Значення семафору може бути встановлено на значення, що перевищує 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, яку можна редагувати :)