/* by josh */

2014年7月14日 星期一

[C#] 多執行緒互斥存取

使用多執行緒執設計的程式有可時會面臨到同一時間不同執行緒會存取同一檔案的情況,此時.NET會跳出由於另一個處理序正在使用檔案…”的例外。

若要避免此情況可使用C#的關鍵字”lock”;在lock區塊中的程式碼被某一個執行緒A占用時,若有其他執行緒B要執行,則必須等到執行緒A離開此區塊後,執行緒B才可進入。


For example
假設有一個Backgroundworker不斷寫入log如下圖:


void BackGroundWorker_writeLog(object sender,DoWorkEventArgs e)
{
 while (true)
 {
               lock (thisLock)
               {
                    objSystemEventLogShow.Show("背景執行緒   "+counter, false);
                    counter++;
               }
 }
}

主執行緒中另一個timer也會寫入log,如下:

private void timer1_Tick(object sender, EventArgs e)
        {
            lock (thisLock)
            {
                objSystemEventLogShow.Show("主執行緒   " + counter, true);
                counter = 1;
            }
        }

因為backgroundworker所產生的執行緒不斷地在寫log,若於backgroundworker正在寫入時,主執行緒timer正好被觸發且要寫入同一個檔案,此時便會產生例外造成程式終止。

為了避免此情況,可在寫log時的取塊中可以加入lock關鍵字;而lock()中的參數則可以想像成是一個mutex,執行緒需取得存取權才能進入函式中,函式執行完畢則釋放存取權。


所以,號誌應該被宣告為全域變數,且最好是靜態的(static)。此例中我在class開頭便做宣告,如下圖:


需要注意的是,Lock的位置必須放對,以上面的例子而言,如果將lock放在while(true)的外面,while迴圈不斷地執行,換句話說backgroundworker並不可能釋放log的資源,如此主執行緒timer將永遠等不到資源可以執行,如此便造成執行緒飢餓(starvation)。如下:

void BackGroundWorker_writeLog(object sender,DoWorkEventArgs e)
        {
            lock (thisLock)
            {
                while (true)
                {
                    objSystemEventLogShow.Show("背景執行緒   " + counter, false);
                    counter++;

                }
            }
        }