Что такое хороший шаблон для использования глобального мьютекса в C#?
класс мьютексов очень неправильно понимается, а глобальные мьютексы тем более.
Что такое хорошо и безопасным для использования при создании глобальных мьютексов?
для работы
- независимо от локали моя машина находится в
- гарантированно освободить мьютекс правильно
- необязательно не зависает навсегда, если мьютекс не приобретается
- имеет дело со случаями, когда другие процессы отказываются от мьютекса
6 ответов:
Я хочу убедиться, что это там, потому что это так трудно получить право:
using System.Runtime.InteropServices; //GuidAttribute using System.Reflection; //Assembly using System.Threading; //Mutex using System.Security.AccessControl; //MutexAccessRule using System.Security.Principal; //SecurityIdentifier static void Main(string[] args) { // get application GUID as defined in AssemblyInfo.cs string appGuid = ((GuidAttribute)Assembly.GetExecutingAssembly(). GetCustomAttributes(typeof(GuidAttribute), false). GetValue(0)).Value.ToString(); // unique id for global mutex - Global prefix means it is global to the machine string mutexId = string.Format( "Global\{{{0}}}", appGuid ); // Need a place to store a return value in Mutex() constructor call bool createdNew; // edited by Jeremy Wiebe to add example of setting up security for multi-user usage // edited by 'Marc' to work also on localized systems (don't use just "Everyone") var allowEveryoneRule = new MutexAccessRule( new SecurityIdentifier( WellKnownSidType.WorldSid , null) , MutexRights.FullControl , AccessControlType.Allow ); var securitySettings = new MutexSecurity(); securitySettings.AddAccessRule(allowEveryoneRule); // edited by MasonGZhwiti to prevent race condition on security settings via VanNguyen using (var mutex = new Mutex(false, mutexId, out createdNew, securitySettings)) { // edited by acidzombie24 var hasHandle = false; try { try { // note, you may want to time out here instead of waiting forever // edited by acidzombie24 // mutex.WaitOne(Timeout.Infinite, false); hasHandle = mutex.WaitOne(5000, false); if (hasHandle == false) throw new TimeoutException("Timeout waiting for exclusive access"); } catch (AbandonedMutexException) { // Log the fact that the mutex was abandoned in another process, // it will still get acquired hasHandle = true; } // Perform your work here. } finally { // edited by acidzombie24, added if statement if(hasHandle) mutex.ReleaseMutex(); } } }
принятый в ответ я создаю вспомогательный класс, чтобы вы могли использовать его аналогичным образом можно использовать оператор Lock. Просто подумал, что поделюсь.
использование:
using (new SingleGlobalInstance(1000)) //1000ms timeout on global lock { //Only 1 of these runs at a time RunSomeStuff(); }и вспомогательный класс:
class SingleGlobalInstance : IDisposable { //edit by user "jitbit" - renamed private fields to "_" public bool _hasHandle = false; Mutex _mutex; private void InitMutex() { string appGuid = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), false).GetValue(0)).Value; 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.Close(); } } }
этот пример завершится через 5 секунд, если уже запущен другой экземпляр.
// unique id for global mutex - Global prefix means it is global to the machine const string mutex_id = "Global\{B1E7934A-F688-417f-8FCB-65C3985E9E27}"; static void Main(string[] args) { using (var mutex = new Mutex(false, mutex_id)) { try { try { if (!mutex.WaitOne(TimeSpan.FromSeconds(5), false)) { Console.WriteLine("Another instance of this program is running"); Environment.Exit(0); } } catch (AbandonedMutexException) { // Log the fact the mutex was abandoned in another process, it will still get aquired } // Perform your work here. } finally { mutex.ReleaseMutex(); } } }
в принятом ответе есть условие гонки, когда 2 процесса работают под 2 разными пользователями, пытающимися инициализировать мьютекс одновременно. После того, как первый процесс инициализирует мьютекс, если второй процесс попытается инициализировать мьютекс до того, как первый процесс установит правило доступа для всех, второй процесс создаст несанкционированное исключение.
смотрите ниже для исправленного ответа:
using System.Runtime.InteropServices; //GuidAttribute using System.Reflection; //Assembly using System.Threading; //Mutex using System.Security.AccessControl; //MutexAccessRule using System.Security.Principal; //SecurityIdentifier static void Main(string[] args) { // get application GUID as defined in AssemblyInfo.cs string appGuid = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), false).GetValue(0)).Value.ToString(); // unique id for global mutex - Global prefix means it is global to the machine string mutexId = string.Format( "Global\{{{0}}}", appGuid ); bool createdNew; // edited by Jeremy Wiebe to add example of setting up security for multi-user usage // edited by 'Marc' to work also on localized systems (don't use just "Everyone") var allowEveryoneRule = new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), MutexRights.FullControl, AccessControlType.Allow); var securitySettings = new MutexSecurity(); securitySettings.AddAccessRule(allowEveryoneRule); using (var mutex = new Mutex(false, mutexId, out createdNew, securitySettings)) { // edited by acidzombie24 var hasHandle = false; try { try { // note, you may want to time out here instead of waiting forever // edited by acidzombie24 // mutex.WaitOne(Timeout.Infinite, false); hasHandle = mutex.WaitOne(5000, false); if (hasHandle == false) throw new TimeoutException("Timeout waiting for exclusive access"); } catch (AbandonedMutexException) { // Log the fact the mutex was abandoned in another process, it will still get aquired hasHandle = true; } // Perform your work here. } finally { // edited by acidzombie24, added if statemnet if(hasHandle) mutex.ReleaseMutex(); } } }
ни мьютекс, ни WinApi CreateMutex() не работает для меня.
альтернативное решение:
static class Program { [STAThread] static void Main() { if (SingleApplicationDetector.IsRunning()) { return; } Application.Run(new MainForm()); SingleApplicationDetector.Close(); } }и
SingleApplicationDetector:using System; using System.Reflection; using System.Runtime.InteropServices; using System.Security.AccessControl; using System.Threading; public static class SingleApplicationDetector { public static bool IsRunning() { string guid = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), false).GetValue(0)).Value.ToString(); var semaphoreName = @"Global\" + guid; try { __semaphore = Semaphore.OpenExisting(semaphoreName, SemaphoreRights.Synchronize); Close(); return true; } catch (Exception ex) { __semaphore = new Semaphore(0, 1, semaphoreName); return false; } } public static void Close() { if (__semaphore != null) { __semaphore.Close(); __semaphore = null; } } private static Semaphore __semaphore; }причина использовать семафор вместо мьютекса:
класс мьютекса обеспечивает идентификацию потока, поэтому мьютекс может быть выпущен только потоком, который его приобрел. Напротив, класс Semaphore не применяет идентификатор потока.
.Нарезка резьбы.Мьютекс
иногда обучение на примере помогает больше всего. Запустите это консольное приложение в трех разных консольных окнах. Вы увидите, что приложение, которое вы запустили первым, сначала получает мьютекс, а два других ждут своей очереди. Затем нажмите enter в первом приложении, вы увидите, что приложение 2 теперь продолжает работать, приобретая мьютекс, однако приложение 3 ждет своей очереди. После нажатия enter в Приложении 2 вы увидите, что приложение 3 продолжается. Это иллюстрирует концепция мьютекса, защищающего раздел кода, который должен выполняться только одним потоком (в данном случае процессом), например, запись в файл в качестве примера.
using System; using System.Threading; namespace MutexExample { class Program { static Mutex m = new Mutex(false, "myMutex");//create a new NAMED mutex, DO NOT OWN IT static void Main(string[] args) { Console.WriteLine("Waiting to acquire Mutex"); m.WaitOne(); //ask to own the mutex, you'll be queued until it is released Console.WriteLine("Mutex acquired.\nPress enter to release Mutex"); Console.ReadLine(); m.ReleaseMutex();//release the mutex so other processes can use it } } }

Comments