2014-01-08 50 views
3

Beeing一個線程noob,我試圖找到一種方式無鎖定對象,允許我排隊一個線程池任務,以這樣的方式,它的最大並行度= 1。Interlocked.Exchange有多安全?

此代碼做我認爲它的作用?

private int status; 
private const int Idle = 0; 
private const int Busy = 1; 

private void Schedule() 
{ 
    // only schedule if we idle 
    // we become busy and get the old value to compare with 
    // in an atomic way (?) 
    if (Interlocked.Exchange(ref status, Busy) == Idle) 
    { 
     ThreadPool.QueueUserWorkItem(Run); 
    } 
} 

也就是說,在排隊的Run方法如果狀態是Idle一個線程安全的方式。 它似乎在我的測試中工作正常,但由於這不是我的區域,我不確定。

+1

沒問題,假設Run()知道要以線程安全的方式選擇什麼工作。你需要一個真正的鎖。將其重新設置爲空閒是更關鍵的操作,因爲即使線程中止並且您可能想要再次等待正在運行的等待作業,也需要保證絕對可靠。 –

+0

謝謝,我在正確的軌道上,然後,運行方法從'ConcurrentQueue'中選擇它的工作' –

回答

1

是的,這將做你想要的。當事實上狀態爲忙時,它絕不會允許您獲得空閒返回值,並且它將在同一操作中將狀態設置爲繁忙,而不會有衝突。到現在爲止還挺好。

但是,如果您以後使用的是ConcurrentQueue<T>,您爲什麼要這樣做?爲什麼使用ThreadPool排隊運行一遍又一遍,而不是讓單個線程不斷使用TryDequeue從併發隊列中取數據?

事實上,有一個專門爲此設計的生產者 - 消費者系列,BlockingCollection<T>。您的消費者線程將只需撥打Take(必要時附帶取消令牌 - 可能是個好主意),並且如果沒有可用值,則返回ConcurrentQueue<T>;中的值,並阻止該線程,直到需要執行某些操作。當其他線程向集合中添加一個項目時,它會通知儘可能多的消費者,因爲它具有數據(在您的情況下,不需要任何複雜的東西,因爲您只有一個消費者)。

這意味着您只需處理啓動和停止單個線程,該線程將運行一個「無限」循環,該循環將呼叫col.Take,而生產者呼叫col.Add

當然,這裏假定你有.NET 4.0+可用,但是你可能會這樣做,因爲你使用的是ConcurrentQueue<T>

+0

我插入線程池的原因是它位於我的actor lib的郵箱類中,所以可以有成千上萬或數百萬個郵箱正在處理郵件,並且由於它們中的很多會在很長一段時間內處於空閒狀態,所以我使用線程池來安排現在實際處於活動狀態的郵箱 –

+0

這是我不使用BlockingCollection的原因,因爲它阻塞..我不能浪費像我這樣的線程資源。 –

+1

@RogerAlsing:我在實踐中沒有嘗試過,但是如果你沒有限制容量的構造函數,它不應該被阻塞。在這種情況下,它應該作爲'ConcurrentQueue '的一個相對簡單的包裝器,它固有地支持通知。總而言之,它不應該使用鎖定(相反,併發隊列和阻塞集合都依賴於使用Interlocked的原子操作,就像你一樣),並且它*不應該被封鎖,只能用於讀取。但是,當然,最終這一切歸結爲剖析:) – Luaan