防止程式同時執行多份,比檢查Process清單更好的方法

在某些情境下,我們需要限制同一支程式同時間只能執行一份,很直覺的想法是檢查Process清單,由程式名稱在清單中出現一次以上來判斷是否已有同名程式在執行。這個做法直覺有效,在大部分情境也適用,甚至在CodeProject上也不乏類似"教學範例",很自然地,這也一度是我愛用過的解法(誰沒有過去呢?);但是,檢查Process清單並非是最簡潔嚴謹的做法。(前述CodeProject文章的評比只有一顆星,留言中不乏負評,這故事告訴我們,爬文時莫急莫慌,至少先看看鄉民怎麼說再決定要不要抄)

ProcessName比對法的缺點不少,更完美的做法是善用Mutex機制,保哥對此有一篇詳盡說明。剛好讀到K. Scott Allen另一篇古老文章,對於ProcessName法的缺點及Mutex應用還有些額外闡述,簡單整理後,也來狗尾續貂一番。

Scott分析了幾個ProcessName法難以應付的"極端案例":

  1. 當人品差到極點,可能出現兩個程式同時開始執行,同時看到對方,同時關閉的情境。
  2. Process.GetProcesses()取得的是全機器的Process清單,難以滿足本機登入及遠端登入(Terminal Service)限制每個登入階段(Login Session)只執行一份的需求。(註: 應有方法依Session再分組,但得花點工)
  3. 若不幸系統中有另一個同名同姓的其他Process,就杯具了。(註: 可以加上路徑比對避免)

因此,利用Mutex的強制排他性,可以輕易實現任何時候只有單一程序成功。而採用GUID或自訂唯一的Mutex名稱,則可防止同名同姓Process的干擾。

Scott文章另外提到了"using (Mutex)包住到程式結束為止"的技巧,目的在避免當後段程式未再使用Mutex物件被GC機制意外回收的極端案例。(註: Scott範例程式中的GC.Collect()是故意加上的,旨在觸發資源回收突顯問題,實務上除非記憶體耗用驚人,Mutex被意外回收的情境並不常出現,但改良版無疑更加完美)

以下是加了註解的簡單範例,展示如何實現程式防止多份同時執行: (移除@"Global\"可做到每個登入階段限定一份)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
 
namespace SingleInstance
{
    class Program
    {
        //每支程式以不同GUID當成Mutex名稱,可避免執行檔同名同姓的風險
        static string appGuid = "{B19DAFCB-729C-43A6-8232-F3C31BB4E404}";
 
        static void Main(string[] args)
        {
           //如果要做到跨Session唯一,名稱可加入"Global\"前綴字
            //如此即使用多個帳號透過Terminal Service登入系統
            //整台機器也只能執行一份
            using (Mutex m = new Mutex(false, "Global\\" + appGuid))
            {
                //檢查是否同名Mutex已存在(表示另一份程式正在執行)
                if (!m.WaitOne(0, false))
                {
                    Console.WriteLine("Only one instance is allowed!");
                    return;
                }
 
                #region 程式處理邏輯
 
                Console.WriteLine("Press Enter to exit...");
                Console.ReadLine();
 
                //如果是Windows Form,Application.Run()要包在using Mutex範圍內
                  //以確保WinForm執行期間Mutex一直存在
 
                #endregion
            }
        }
    }
}
歡迎推文分享:
Published 15 January 2013 09:23 PM 由 Jeffrey
Filed under: ,
Views: 20,813



意見

# Houdini said on 22 January, 2013 08:58 PM

黑大, 跟您請教一個問題:

最近寫一個 Winform 程式也是須要判斷唯一process,

但是因為某些需求必須由程式自動重新啟動 ( Application.restart() ),

但是這樣會造成唯一process判斷程式已執行而重啟失敗,

(判斷應該是 restart 會先重啟新process 再關閉原 process,

導致同時兩個 process 產生 )

不知道黑大是否有解決的方法?

謝謝!

# Jeffrey said on 23 January, 2013 12:01 AM

to Houdini, 我有找到類似情境的討論: bbs.csdn.net/.../320149153

# Houdini said on 24 January, 2013 10:38 PM

感謝黑大! 經測試後那篇是可以 work 的!

在  Application.Restart() 前先做

Program.Mutex.Close() 即可

( Program.cs:   public static Mutex Mutex;

)

# Morris said on 24 January, 2013 10:39 PM

我之前有用過 Mutex 的方式來做...

但在本機開發時..(Windows 7)都可正常判斷..

但程式移到 Win Srv 2008 R2 後就是不正常..判斷不出..

當時也找不出為什麼.

你的看法呢?

(必要的) 
(必要的) 
(選擇性的)
(必要的) 
(提醒: 因快取機制,您的留言幾分鐘後才會顯示在網站,請耐心稍候)

5 + 3 =

搜尋

Go

<January 2013>
SunMonTueWedThuFriSat
303112345
6789101112
13141516171819
20212223242526
272829303112
3456789
 
RSS
創用 CC 授權條款
【廣告】
twMVC
最新回應

Tags 分類檢視
關於作者

一個醉心技術又酷愛分享的Coding魔人,十年的IT職場生涯,寫過系統、管過專案, 也帶過團隊,最後還是無怨無悔地選擇了技術鑽研這條路,近年來則以做一個"有為的中年人"自許。

文章典藏
其他功能

這個部落格


Syndication