Quantcast
Channel: 黑暗執行緒
Viewing all articles
Browse latest Browse all 2447

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

$
0
0

在某些情境下,我們需要限制同一支程式同時間只能執行一份,很直覺的想法是檢查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名稱,可避免執行檔同名同姓的風險
staticstring appGuid = "{B19DAFCB-729C-43A6-8232-F3C31BB4E404}";
 
staticvoid 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
            }
        }
    }
}

Viewing all articles
Browse latest Browse all 2447

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>