2015-06-15 136 views
11

我有一些代碼,在我需要在主線程上確認和處理的新線程上拋出異常。爲了實現這一點,我通過使用一個包含引發異常的字段來共享線程之間的狀態。檢查是否爲空線程安全?

我的問題是當我在下面的代碼示例中檢查時,是否需要使用鎖來檢查null

public class MyClass 
{ 
    readonly object _exceptionLock = new object(); 
    Exception _exception; 

    public MyClass() 
    { 
     Task.Run(() => 
     { 
      while (CheckIsExceptionNull()) 
      { 
       // This conditional will return true if 'something has gone wrong'. 
       if(CheckIfMyCodeHasGoneWrong()) 
       { 
        lock(_exceptionLock) 
        { 
         _exception = new GoneWrongException(); 
        } 
       } 
      } 
     }); 
    } 

    bool CheckIsExceptionNull() // Is this method actually necessary? 
    { 
     lock (_exceptionLock) 
     { 
      return _exception == null; 
     } 
    } 

    // This method gets fired periodically on the Main Thread. 
    void RethrowExceptionsOnMainThread() 
    { 
     if (!CheckIsExceptionNull()) 
     { 
      lock (_exceptionLock) 
      { 
       throw _exception; // Does this throw need to be in a lock? 
      } 
     } 
    } 
} 

此外,我需要使用鎖在主線程拋出異常的時候?

+0

任何時間的問題是 「我是否需要使用鎖」,這是可疑的。一個無爭議的鎖需要十幾納秒。你的問題是「我的程序已經保證是正確的,但是十幾納秒太慢了,這個性能問題可能歸因於一個鎖,並且我知道它在市場上不會成功,所以我可以刪除它這個鎖,並保持我的程序的正確性?「。你的代碼甚至沒有正確的開始,我非常懷疑在無爭議的鎖中花費的納秒會導致你的程序被認爲太慢。 –

+1

一個更好的問題完全是「如何通過使用任務並行庫來表達異步執行任務的成功和失敗的概念,從而消除我對顯式線程管理和共享內存的使用?」。 Task對象已經擁有這樣的屬性,在執行任務期間引發的異常可以安全地緩存並重新引發到管理任務的執行上下文中。 –

回答

9

首先要注意的事情是,你的代碼是不是線程安全的,因爲你有一個線程的比賽:你在不同的鎖定區域,以你扔在那裏檢查CheckIsExceptionNull,但:的值可以在測試和之間切換扔

字段訪問不保證是線程安全的。特別是,雖然引用不能被撕裂(這一點是保障 - 閱讀和引用的寫入是原子),它是保證不同的線程將看到最新的值,由於CPU高速緩存等,這是不太可能實際上咬你,但是這與在一般情況下線程問題的問題,p

就個人而言,我可能只是使現場揮發,並使用本地。例如:

var tmp = _exception; 
if(tmp != null) throw tmp; 

上面沒有線程競賽。添加:

volatile Exception _exception; 

確保值不會在寄存器中緩存的(儘管這在技術上是一個副作用,而不是volatile預期/記錄的效果)

+1

「坦率地說,我勸你不要做一個動盪的領域」,Eric Lippert:(http://stackoverflow.com/a/17530556/7122)。請不要鼓勵它的使用。 –

+0

@DavidArno我不確定添加'lock','Thread.VolatileRead'或者一些'Interlocked'是否會提高可讀性,但是... –

+0

@DavidArno如果你知道自己在做什麼,認爲在SO上提出線程問題的人並不常見。 –

0

可能有很多有趣且有用的關於這樣的問題的信息。你可以花幾個小時閱讀有關它的博客文章和書籍。即使是真正瞭解它的人也最不贊同。 (正如你在Marc Gravell和Eric Lippert的帖子中看到的那樣)。

所以這個問題沒有從我這裏回答。只是一個建議:總是使用鎖。只要多於一個線程訪問字段。沒有風險,沒有猜測,也沒有討論編譯器和CPU技術。只需使用鎖並保存並使其不僅在大多數情況下在您的機器上工作,而且在任何情況下在每臺機器上都可以工作。

延伸閱讀:

+0

在「總是使用鎖」旁邊 - 我發現自己越來越多地使用'鎖'的'互鎖' - 但我傾向於做一些非常有爭議的系統。我的觀點是:'聯鎖'也是「沒有風險,沒有猜測,沒有CPU技術」等。 –

+0

是的,可能。我還沒有看到Interlocked實際上很有用的許多情況,因爲它非常有限。我想有一些地方適合完美。 –