2013-10-03 89 views
1

我有一個多線程問題。退出循環,如果另一個線程輸入在

我有一個方法被調用來刷新幾個項目。

在這種方法中,我遍歷項目列表並刷新它的一個屬性。

該列表有很多元素,我們必須做一些數學計算它的屬性。

此操作的當前的代碼如下所示:

public void AddItemsWithLayoutRefresh(IEnumerable<MyItem> items){ 
    _control.Invoke(()=>{ 
     AddItems(items); 
     for(int i =0;i<_guiItems.Count;i++){ 
      //The goal is to have a condition here to "break" the loop and let the next call to RefreshLayout proceed 
      _guiItems[i].Propriety = ComputePropriety(_guiItems[i]); 
     } 
    }); 
} 

的問題是,我可以有4個電話,這是目前剛剛上阻塞Invoke。 我必須完成「AddItems」方法,但是關於「for」循環中的所有內容,如果我知道它將在後面執行,我可以放棄這個,不會有任何問題。

但如何以線程安全的方式做到這一點?

如果我把private bool _isNewRefreshHere;設置爲真,然後再輸入調用,然後檢查Invoke,我不保證沒有兩個調用已經達到Invoke BEFORE我在for循環中檢查它。

那麼我怎樣才能當我在我的循環,當一個新的電話是我的方法?

解決方案 基於安德烈Mohar的答案,我做了以下內容:

private long m_refreshQueryCount; 
public void AddItemsWithLayoutRefresh(IEnumerable<MyItem> items){ 
    Interlocked.Increment(ref m_refreshQueryCount); 
    _control.Invoke(()=>{ 
     Interlocked.Decrement(ref m_refreshQueryCount); 
     AddItems(items); 
     for(int i =0;i<_guiItems.Count;i++){ 
      if (Interlocked.Read(ref m_refreshQueryCount) > 0) 
      { 
       break; 
      } 
      _guiItems[i].Propriety = ComputePropriety(_guiItems[i]); 
     } 
    }); 
} 

這似乎是非常漂亮的工作

+0

你爲什麼不使用'lock'? – Mehraban

+0

鎖在哪裏? Invoke已經確保只有一個正在運行的進程。我想要的是停止當前的調用,如果一個新的調用 – J4N

+0

順便說一下,你是添加**佈局刷新多個項目**?它只提供性能降低。 「Suspend/Resume」佈局有什麼問題? – AgentFire

回答

1

如果我是你,我會嘗試做一個線程安全的等待計數器。您可以使用Interlocked方法,如遞增和遞減。這些基本上做的是他們增加價值作爲原子操作,被認爲是線程安全的。所以你在Invoke調用之前增加變量。這將允許您知道等待隊列中有多少個線程。在for循環結束之後並且在Invoke塊結束之前,您將該變量遞減。然後,您可以在for語句中檢查等待線程的數量,如果數字大於1,則檢查for。這樣,您應該確切知道執行鏈中有多少個線程。

+0

這實際上是爲什麼['Semaphore'](http://msdn.microsoft.com/en-us/library/system.threading.semaphore。 aspx)是爲創建的。 – AgentFire

+0

@Andrej Mohar:謝謝,這就是我要找的!我自己開始執行這種行爲,帶有一個鎖和一個int! AgendFire:我沒有看到一種方法來查看當前在信號量上等待的線程數量嗎?就我而言,它是具有信號量的線程,它應該檢查是否有其他線程正在等待。 – J4N

+0

@AgentFire:你確定嗎?嗯,我可能已經理解了信號量的錯誤。我認爲信號量可以觀察代碼的受保護部分中可以運行多少個線程。我正在考慮更多地使代碼的檢查部分能夠顯示等待的線程數量,並根據該數量根據需要中斷執行。 –

1

我會做下列方式:

private readonly object _refresherLock = new object(); 
private bool _isNewRefreshHere = false; 
private AutoResetEvent _refresher = new AutoResetEvent(true); 

public void AddItemsWithLayoutRefresh(IEnumerable<MyItem> items) 
{ 
    lock (_refresherLock) 
    { 
     if (_isNewRefreshHere) 
     { 
      return; 
     } 

     _isNewRefreshHere = true; 
    } 

    _refresher.WaitOne(); 
    _isNewRefreshHere = false; 

    _control.Invoke(() => 
    { 
     AddItems(items); 

     for (int i = 0; i < _guiItems.Count && !_isNewRefreshHere; i++) 
     { 
      _guiItems[i].Propriety = ComputePropriety(_guiItems[i]); 
     } 

     _refresher.Set(); 
    }); 
} 

即:

  • 您可以隨時取消當前的更新。
  • 一次不能排隊多個更新。
  • 您保證沒有交叉線程衝突。
  • 您應該測試該代碼,因爲我沒有。 :)
+0

這可能是一個好主意,但我需要不返回我的方法,直到刷新正確完成。 – J4N

+0

@ J4N你是什麼意思? – AgentFire

+0

如果我有3個線程,A,B和C,A將進入,B和C進入非常快,B獲勝並將'_isNewRefreshHere'設置爲true,C進來,看到'_isNewRefreshHere'設置爲true並返回,但佈局尚未刷新。因此,調用者會認爲即使不是這種情況,一切都已被正確刷新,即使情況並非如此 – J4N

相關問題