是否使用Mutex来防止同一程序的多个实例运行安全?

Mal*_*ist 45 c# mutex single-instance winforms

我正在使用此代码阻止我的程序的第二个实例同时运行,是否安全?

Mutex appSingleton = new System.Threading.Mutex(false, "MyAppSingleInstnceMutx");
if (appSingleton.WaitOne(0, false)) {
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new MainForm());
    appSingleton.Close();
} else {
    MessageBox.Show("Sorry, only one instance of MyApp is allowed.");
}
Run Code Online (Sandbox Code Playgroud)

我担心如果有什么东西抛出一个异常并且应用程序崩溃了Mutex仍然会被保留.真的吗?

Ant*_*hyy 61

为此目的使用Windows事件更为常见和方便.例如

static EventWaitHandle s_event ;

bool created ;
s_event = new EventWaitHandle (false, 
    EventResetMode.ManualReset, "my program#startup", out created) ;
if (created) Launch () ;
else         Exit   () ;
Run Code Online (Sandbox Code Playgroud)

当您的进程退出或终止时,Windows将为您关闭该事件,如果没有打开的句柄,则将其销毁.

补充:管理事件(或互斥)名称的会话,使用Local\Global\前缀.如果您的应用程序是每个用户,只需将一个适当受损的登录用户名称附加到事件名称.

  • 您可以以相同的方式使用互斥锁.也就是说,只需使用createdNew标志来确定您的是否是第一个实例.无需在互斥锁上进行同步.因此,EventWaitHandle方法并不比Mutex方法更好或更差.你也可以使用Semaphore. (11认同)
  • 这比99%的解决方案简单得多,但这是我见过的唯一一次.谢谢! (3认同)
  • 虽然更方便,甚至更安全、更干净,但这种方法不跨平台兼容。当使用.NET Core开发应用程序时,此代码会抛出异常。([来源](https://github.com/dotnet/coreclr/blob/release/2.2/src/mscorlib/src/System/Threading/EventWaitHandle.cs#L51)) (2认同)

Jar*_*Par 42

一般来说是的,这将有效.然而,魔鬼在于细节.

首先,您要关闭finally块中的互斥锁.否则,您的进程可能会突然终止并使其处于信号状态,例如异常.这将使未来的流程实例无法启动.

不幸的是,即使有一个finally块,你也必须处理在不释放互斥锁的情况下终止进程的可能性.例如,如果用户通过TaskManager杀死进程,就会发生这种情况.有一个在你的代码中的竞争条件,将允许第二处理,得到AbandonedMutexExceptionWaitOne通话.你需要一个恢复策略.

我鼓励您阅读Mutex类的详细信息.使用它并不总是很简单.


扩大竞争条件的可能性:

可能发生以下事件序列,这将导致应用程序的第二个实例抛出:

  1. 正常流程启动.
  2. 第二个进程启动并获取互斥锁的句柄,但在WaitOne调用之前被切换出来.
  3. 进程#1突然终止.由于进程#2有句柄,因此不会破坏互斥锁.而是设置为废弃状态.
  4. 第二个进程再次开始运行并得到一个AbanonedMutexException.

  • 如果他的代码是唯一创建互斥锁的代码,那么当进程终止时它将被销毁,因此create + waitOne将成功. (2认同)

Ste*_*fan 11

您可以使用互斥锁,但首先要确保这是您想要的.

因为"避免多个实例"没有明确定义.这可能意味着

  1. 避免在同一用户会话中启动多个实例,无论用户会话具有多少个桌面,但允许多个实例同时针对不同的用户会话运行.
  2. 避免在同一桌面上启动多个实例,但只要每个实例位于单独的桌面中,就允许多个实例运行.
  3. 避免为同一用户帐户启动多个实例,无论存在多少台桌面或此帐户下运行的会话,但允许多个实例同时运行以在不同用户帐户下运行的会话.
  4. 避免在同一台计算机上启动多个实例.这意味着无论任意数量的用户使用了多少台桌面,最多只能运行一个程序实例.

通过使用互斥锁,您基本上使用的是定义编号4.


小智 10

我使用这种方法,我认为它是安全的,因为如果任何应用程序都没有长时间保持Mutex被破坏(如果它们最初不能创建Mutext则应用程序被终止).这可能在"AppDomain-processes"中有相同或不同的作用(参见下面的链接):

// Make sure that appMutex has the lifetime of the code to guard --
// you must keep it from being collected (and the finalizer called, which
// will release the mutex, which is not good here!).
// You can also poke the mutex later.
Mutex appMutex;

// In some startup/initialization code
bool createdNew;
appMutex = new Mutex(true, "mutexname", out createdNew);
if (!createdNew) {
  // The mutex already existed - exit application.
  // Windows will release the resources for the process and the
  // mutex will go away when no process has it open.
  // Processes are much more cleaned-up after than threads :)
} else {
  // win \o/
}
Run Code Online (Sandbox Code Playgroud)

以上内容来自其他答案/评论中关于恶意程序能够坐在互斥锁上的注释.这里不是问题.此外,在"本地"空间中创建了未加前缀的互斥锁.这可能是正确的事情.

请参阅:http://ayende.com/Blog/archive/2008/02/28/The-mysterious-life-of-mutexes.aspx - Jon Skeet随附;-)