2008-09-29 23 views
3

我有一個方法應該以獨家方式執行。基本上,這是一個多線程應用程序,其中該方法由定時器定期調用,但也可以由用戶操作手動觸發。如何在多線程上下文中獨佔方法?

讓我們舉個例子:

  1. 的定時器結束,因此該方法是 調用。該任務可能需要幾秒鐘的時間。

  2. 之後,用戶點擊一些 按鈕,這應該會觸發 相同的任務:BAM。由於該方法已在運行,因此它不執行任何操作 。

我用以下解決方案:

public void DoRecurentJob() 
{ 
    if(!Monitor.TryEnter(this.lockObject)) 
    { 
     return; 
    } 

    try 
    { 
     // Do work 
    } 
    finally 
    { 
     Monitor.Exit(this.lockObject); 
    } 
} 

lockObject聲明那樣:

private readonly object lockObject = new object(); 

編輯:將有哪些持有該對象的只有一個實例方法,所以我將鎖對象更新爲非靜態的。

有沒有更好的方法來做到這一點?或者,也許這是一個錯誤的任何理由?

回答

4

這看起來很合理,如果你只是對沒有方法並行運行感興趣。沒有什麼可以阻止它在彼此之後立即運行,比如說在定時器執行Monitor.Exit()之後半個微秒按下按鈕。

並且將鎖對象設置爲只讀靜態也是有意義的。

2

如果您希望它跨越進程(略有性能損失),或者您需要設置任何其他數字,而不是允許的併發線程運行您的代碼段,您也可以使用MutexSemaphore

還有其他的信令結構可以工作,但你的例子看起來像它的伎倆,並以一種簡單而直接的方式。

+0

注意的是互斥使用內核資源重量級的對象,如果你需要輸入鎖跨進程的工作應僅使用。顯示器更輕更快。在'Accelerated C#2008'中,Trey Nash寫道:「一個簡單的測試表明,使用Mutex的時間比Monitor類長34倍。」 – 2008-09-29 20:24:17

+0

優化速度聽起來非常不成熟。 – bzlm 2008-10-01 11:50:09

2

小問題:如果lockObject變量是靜態的,那麼「this.lockObject」不應該編譯。它也有點奇怪(至少應該大量記錄),雖然這是一種實例方法,但它也具有明顯的類型行爲。可能使它成爲一個以實例爲參數的靜態方法?

它實際使用實例數據嗎?如果沒有,請將其設爲靜態。如果是這樣,你至少應該返回一個布爾值來表示你是否對這個實例進行了工作 - 我發現很難想象這種情況,我希望用某個特定的數據完成一些工作,但是我沒有請注意,如果因爲一些類似的工作正在使用不同的數據執行某項工作而未執行該工作。

我認爲它應該工作,但它確實感覺有點奇怪。我通常不喜歡使用手動鎖定,僅僅因爲它很容易出錯 - 但這看起來沒問題。 (你需要考慮「if」和「try」之間的異步異常,但我懷疑它們不會成爲問題 - 我不記得CLR做出的確切保證。)

+0

你對「這個」完全正確。前綴,我複製/粘貼了我的代碼後添加了它:「糟糕,我應該更好地將前綴lockObject設置爲」this「,以使我的示例對於計算器讀者更加清晰:) :) – 2008-09-29 09:57:22

1

代碼很好,但會同意將方法更改爲靜態,因爲它更好地傳達了意圖。奇怪的是,一個類的所有實例都有一個同步運行的方法,但該方法不是靜態的。

請記住,您始終可以將靜態同步方法設置爲受保護或私有方式,並且只允許該類的實例可見。

public class MyClass 
{ 
    public void AccessResource() 
    { 
     OneAtATime(this); 
    } 

    private static void OneAtATime(MyClass instance) 
    { 
     if(!Monitor.TryEnter(lockObject)) 
     // ... 
0

這是一個很好的解決方案,雖然我對靜態鎖並不滿意。現在你不會等待鎖定,所以你不會陷入死鎖的困境。但是,在下次編輯此代碼時,使鎖顯示可​​能很容易讓您陷入困境。這也不是一個可擴展的解決方案。

我通常會嘗試製作所有資源,以防止被多個線程的私有實例變量訪問,然後將鎖作爲私有實例變量。這樣,如果需要縮放,可以實例化多個對象。

1

我認爲微軟recommends使用lock聲明,而不是直接使用Monitor類。它提供了一個更清潔的佈局,並確保在任何情況下鎖都被釋放。

public class MyClass 
{ 

    // Used as a lock context 
    private readonly object myLock = new object(); 

    public void DoSomeWork() 
    { 
    lock (myLock) 
    { 
     // Critical code section 
    } 
    } 
} 

如果應用程序需要鎖跨越MyClass的所有情況下,您可以定義爲靜態字段的鎖定方面:這樣做是利用在MethodImplOptions.Synchronized符的

private static readonly object myLock = new object(); 
0

更聲明式的方式該方法以你希望同步訪問:

[MethodImpl(MethodImplOptions.Synchronized)] 
public void OneAtATime() { } 

然而,這種方法不鼓勵由於幾個原因,其中大部分可以找到herehere。我發佈這個,所以你不會覺得很想用它。在Java中,​​是一個關鍵字,所以在查看線程模式時可能會出現。

0

我們有一個類似的需求,要求如果再次請求長時間運行的進程,它應該在當前週期完成後排隊執行另一個週期。它類似於此:

https://codereview.stackexchange.com/questions/16150/singleton-task-running-using-tasks-await-peer-review-challenge

private queued = false; 
private running = false; 
private object thislock = new object(); 

void Enqueue() { 
    queued = true; 
    while (Dequeue()) { 
     try { 
      // do work 
     } finally { 
      running = false; 
     } 
    } 
} 

bool Dequeue() { 
    lock (thislock) { 
     if (running || !queued) { 
      return false; 
     } 
     else 
     { 
      queued = false; 
      running = true; 
      return true; 
     } 
    } 
} 
相關問題