Mutex類很是容易被誤解,而全局互斥體更是如此。 安全
建立全局互斥鎖時,可使用哪一種良好,安全的模式? app
一個會起做用的 學習
若是另外一個實例已在運行,則此示例將在5秒鐘後退出。 ui
// 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(); } } }
Mutex和WinApi CreateMutex()都不適合我。 this
替代解決方案: spa
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; }
使用信號量而不是Mutex的緣由: code
Mutex類強制執行線程標識,所以互斥鎖只能由獲取該互斥鎖的線程釋放。 相反,信號量類不強制執行線程身份。 orm
<< System.Threading.Mutex 進程
我想確保這是正確的,由於很難正確地作到這一點:
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(); } } }
當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(); } } }
有時經過榜樣學習最有幫助。 在三個不一樣的控制檯窗口中運行此控制檯應用程序。 您會看到,首先運行的應用程序首先獲取了互斥鎖,而其餘兩個正在等待輪換。 而後在第一個應用程序中按Enter鍵,您將看到經過獲取互斥鎖如今能夠繼續運行應用程序2,可是應用程序3正在等待輪換。 在應用程序2中按Enter鍵後,您將看到應用程序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 } } }