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 } } }