Чи може хтось пояснити різницю між:
- замок (деякийоб'єкт) {}
- Використання Mutex
- Використання Семафору
- Використання монітора
- Використання інших класів синхронізації .Net
Я просто не можу це зрозуміти. Мені здається, перші два такі ж?
Чи може хтось пояснити різницю між:
Я просто не можу це зрозуміти. Мені здається, перші два такі ж?
Відповіді:
Чудове запитання. Я, можливо, помиляюсь. Дозвольте спробувати .. Редакція №2 моєї відповіді "Оріс". Дякуємо, що змусили мене читати :)
замок (obj)
Монітори
Використання блокування або монітора корисно для запобігання одночасному виконанню потокових блоків коду, але ці конструкції не дозволяють одному потоку повідомляти подію іншому. Для цього потрібні події синхронізації , які є об'єктами, що мають один із двох станів, сигналізований та несигналізований, які можна використовувати для активації та призупинення потоків. Mutex, Semaphores - це концепції на рівні ОС. наприклад, з іменованим mutex ви можете синхронізувати через декілька (керованих) exe (гарантуючи, що на пристрої працює лише один екземпляр вашої програми.)
Mutex:
Семафори (боляче мій мозок).
Monitor
не дозволяє комунікація невірна; Ви все ще можете і Pulse
т. д. зMonitor
Повторно "Використання інших класів синхронізації .Net" - деякі з інших, про які вам слід знати:
У CCR / TPL ( Parallel Extensions CTP) є також більше (низькі накладні) замикаючі конструкції - але IIRC, вони будуть доступні в .NET 4.0
Як зазначено в ECMA, і як ви можете помітити з Reflected методів, твердження про блокування в основному еквівалентно
object obj = x;
System.Threading.Monitor.Enter(obj);
try {
…
}
finally {
System.Threading.Monitor.Exit(obj);
}
З вищезгаданого прикладу ми бачимо, що Монітори можуть блокувати об’єкти.
Mutexe корисні, коли вам потрібна міжпроцесова синхронізація, оскільки вони можуть фіксуватися на ідентифікаторі рядка. Один і той же ідентифікатор рядка може використовуватися різними процесами для придбання блокування.
Семафори схожі на мутекси на стероїдах, вони дозволяють одночасний доступ, забезпечуючи максимальну кількість одночасного доступу '. Після досягнення межі семафор починає блокувати подальший доступ до ресурсу, поки один з абонентів не звільнить семафор.
Я робив класи та підтримку CLR для потоків в DotGNU, і у мене є кілька думок ...
Якщо вам не потрібні перехресні блокування процесу, ви завжди повинні уникати використання Mutex & Semaphores. Ці класи в .NET є обгортками навколо Win32 Mutex і Semaphores і мають досить велику вагу (для них потрібен контекстний перехід в ядро, що дорого - особливо якщо ваш замок не суперечить).
Як згадуються інші, заява блокування C # є магією компілятора для Monitor.Enter та Monitor.Exit (існує в межах спроби / нарешті).
Монітори мають простий, але потужний механізм сигналу / очікування, якого у Mutexes немає за допомогою методу Monitor.Pulse / Monitor.Wait. Еквівалент Win32 буде об'єктами події через CreateEvent, які фактично також існують у .NET як WaitHandles. Модель Pulse / Wait схожа на pthread_signal та pthread_wait Unix, але вони швидші, тому що вони можуть бути цілком операційними в користувальницькому режимі в непротиворечатому випадку.
Monitor.Pulse / Wait простий у використанні. В одному потоці ми замикаємо об'єкт, перевіряємо прапор / стан / властивість, і якщо це не те, що ми очікуємо, зателефонуйте на Monitor. Зачекайте, що вимкне замок і чекаємо, поки імпульс буде надісланий. Коли очікування повернеться, ми повертаємося назад і ще раз перевіряємо прапор / стан / властивість. В іншому потоці ми блокуємо об'єкт щоразу, коли ми змінюємо прапор / стан / властивість, а потім викликаємо PulseAll, щоб прокинути будь-які потоки прослуховування.
Часто ми хочемо, щоб наші класи були безпечними для потоків, тому ми ставимо замки в свій код. Однак часто трапляється так, що наш клас коли-небудь буде використовуватися лише однією ниткою. Це означає, що блокування непотрібно уповільнює наш код ... саме тут розумні оптимізації в CLR можуть допомогти підвищити продуктивність.
Я не впевнений у впровадженні замків Microsoft, але в DotGNU та Mono прапор стану блокування зберігається у заголовку кожного об’єкта. Кожен об’єкт у .NET (та Java) може стати замком, тому кожен об’єкт повинен підтримувати це у своєму заголовку. У реалізації DotGNU є прапор, який дозволяє використовувати глобальний хештель для кожного об'єкта, який використовується як замок - це має перевагу усунення 4-байтових накладних даних для кожного об’єкта. Це не чудово для пам’яті (особливо для вбудованих систем, які не мають великої кількості потоків), але є враженням у продуктивності.
Як Mono, так і DotGNU ефективно використовують мьютекси для виконання блокування / очікування, але використовують стилі спіклок порівняння та обміну щоб усунути необхідність фактично виконувати жорсткі блокування, якщо насправді це не потрібно:
Ви можете побачити приклад того, як можна реалізувати монітори тут:
http://cvs.savannah.gnu.org/viewvc/dotgnu-pnet/pnet/engine/lib_monitor.c?revision=1.7&view=markup
Додатковим застереженням для блокування будь-якого спільного Mutex, який ви ідентифікували з ідентифікатором рядка, є те, що він буде за замовчуванням для "локального \" мутексу та не буде поділятися протягом сеансів у середовищі сервера терміналів.
Поставте свій ідентифікатор рядка за допомогою "Глобального \", щоб забезпечити належний контроль доступу до спільних системних ресурсів. Я просто зіткнувся з цілою купою проблем із синхронізацією зв’язку зі службою, що працює під обліковим записом SYSTEM, перш ніж я зрозумів це.
Я б спробував уникнути "lock ()", "Mutex" та "Monitor", якщо можете ...
Ознайомтеся з новим простором імен System.Collections.Concurrent в .NET 4 У
ньому є кілька приємних класів колекцій, безпечних для потоків.
http://msdn.microsoft.com/en-us/library/system.collections.concurrent.aspx
Одночасні словникові скелі! більше немає ручного блокування для мене!
У більшості випадків не слід використовувати замки (= Монітори) або мутекси / семафори. Всі вони блокують поточну нитку.
І ви точно не повинні використовувати System.Collections.Concurrent
класи - вони є основним джерелом перегонових умов, оскільки не підтримують транзакції між декількома колекціями, а також блокують поточну нитку.
Дивно. NET не має ефективних механізмів синхронізації.
Я реалізував послідовну чергу від GCD ( Objc/Swift
world) на C # - дуже легкий, не блокуючий інструмент синхронізації, що використовує пул потоків, з тестами.
Це найкращий спосіб синхронізувати що-небудь у більшості випадків - від доступу до бази даних (привіт sqlite) до бізнес-логіки.