Mutex类被误解了,而Global mutex则更是如此.
在创建全局互斥锁时使用什么是好的,安全的模式?
一个会起作用的
无论我的机器所在的语言环境如何
保证正确释放互斥锁
如果未获取互斥锁,则可选择不会永久挂起
处理其他进程放弃互斥锁的情况
Sam Saffron.. 393
我想确保它在那里,因为它很难做到:
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(); } } }
提示:注意使用Mutex和ASP.NET:"Mutex类强制执行线程标识,因此互斥锁只能由获取它的线程释放.相比之下,Semaphore类不强制执行线程标识." ASP.NET请求可以由多个线程提供服务. (2认同)
deepee1.. 125
使用接受的答案我创建了一个帮助器类,因此您可以使用与使用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(); } } }
超时0应该仍然是零的超时,而不是无限!更好地检查"<0"而不是"<= 0". (3认同)
@antistar:我发现在Dispose方法中使用`_mutex.Close()`代替`_mutex.Dispose()`对我有用.尝试处理底层的WaitHandle导致错误.`Mutex.Close()`处理底层资源. (2认同)
Van Nguyen.. 12
当在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(); } } }
请注意,此问题现已在接受的答案中修复. (7认同)
Liam.. 10
如果另一个实例已在运行,此示例将在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(); } } }
小智.. 8
Mutex和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; }
使用信号量而不是互斥锁的原因:
Mutex类强制执行线程标识,因此互斥锁只能由获取它的线程释放.相比之下,Semaphore类不强制执行线程标识.
<< System.Threading.Mutex
Ref:Semaphore.OpenExisting()
我想确保它在那里,因为它很难做到:
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(); } } }
当在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(); } } }
如果另一个实例已在运行,此示例将在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(); } } }
Mutex和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; }
使用信号量而不是互斥锁的原因:
Mutex类强制执行线程标识,因此互斥锁只能由获取它的线程释放.相比之下,Semaphore类不强制执行线程标识.
<< System.Threading.Mutex
Ref:Semaphore.OpenExisting()