2016-03-07 78 views
-6

我很好奇,如果可以安全地在for循環中做一個空的while循環,以便阻止它繼續到x爲真,在for循環中做一個空的while循環是否安全?

例如(更新爲下載爲例):

string[] links = new string[3] {"http://url.com/download1.rar", "http://url.com/download2.rar", "http://url.com/download3.rar"}; 

public void download(String link) { 
    // download link 
    x = false; 
} 

再後來某處

for (int i; i < links.length; i++) { 
    download(links[i]); 
    x = true; 
    while (x) { 
    // Do nothing so the for loop basically freezes until download is finished 
    } 

    // Do something else or just end it here and continue the loop 
} 

請注意:這是一個簡單的例子,下載已經完全無關的問題,我只是試圖幫助你幫助我理解這一點

或者有沒有更好的方法來完成這個?

編輯:編輯的代碼,以顯示我在想什麼

+0

我認爲它會被卡在那裏,嘗試添加一個'突破; '語句來確保它會離開while循環 –

+2

這是什麼?這種結構對於多線程應用程序是有意義的,但多線程有很多更好的方法來等待單獨的工作完成:互斥量,信號量等 – Gobra

+4

當這段代碼永遠循環時,你期望什麼代碼會讓x爲真?代碼是如何運行的? –

回答

3

編輯一個更好的例子:更新完全改變的問題。

如果您在循環中更新x並且未從外部線程更改,則程序將鎖定並且不會繼續,因爲代碼無法到達x = true,因爲它正被while循環阻止。


即將用盡100%的CPU在單核上執行0次有用的工作,你認爲那好嗎?另外,如果x未標記爲volitile,即使您更新x,它也可能會永久旋轉。

更好的方法是使用某種互斥鎖來阻止代碼,直到您準備好。例如一個ManualResetEventSlim

private ManualResetEventSlim _block = new ManualResetEventSlim(); 

public void A() 
{ 
    for (int i; i < args.length; i++) 
    { 
     _block.Wait(); //This code blocks till UnblockA() is called. 

     // Do something 
    } 
} 

public void UnblockA() 
{ 
    _block.Set(); 
} 

public void BlockA() 
{ 
    _block.Reset(); 
} 
+0

我更新了這個問題,你介意再次看看嗎? –

+0

看看你的更新後的代碼,它看起來像'下載('直到它完成下載纔會返回,所以while循環沒有意義,它已經不會繼續,因爲你的函數調用被阻塞了,我建議用一個完整的可編輯的例子你你正在做什麼,並解釋爲什麼它不工作 –

+0

我什麼都沒做,所以沒有什麼真正的「不工作」在這裏,我只是(如主題中所述)嘗試這樣做是有道理的,因爲當我考慮它時它有點讓人困惑:-) –

1

直接回答你的問題:不,這不是普遍安全的。見這個優秀的系列文章的部分「記憶障礙及波動性」在.NET線程:http://www.albahari.com/threading/part4.aspx#_Memory_Barriers_and_Volatility

具體做法是:

「我們真的需要鎖和障礙

與共享可寫域工作?關於這個主題有很多誤導性的信息 - 包括MSDN文檔,其中指出MemoryBarrier只在具有弱內存排序的多處理器系統上是必需的,例如採用多個Itanium處理器的系統。內存屏障對使用f的普通Intel Core-2和Pentium處理器非常重要執行短程序。您需要啓用優化和沒有調試器中運行(在Visual Studio中,選擇發佈模式的解決方案的配置管理器,然後啓動無需調試):

static void Main() 
{ 
    bool complete = false; 
    var t = new Thread (() => 
    { 
    bool toggle = false; 
    while (!complete) toggle = !toggle; 
    }); 
    t.Start(); 
    Thread.Sleep (1000); 
    complete = true; 
    t.Join();  // Blocks indefinitely 
} 

這個程序永遠不會終止,因爲完整的變量被高速緩存在CPU寄存器中。在while循環中插入對Thread.MemoryBarrier的調用(或在讀取完成時鎖定)可以修復錯誤。「

及更高版本:

」碰巧,英特爾的X86和X64處理器總是適用獲取柵欄,以讀取和釋放柵欄來寫 - 無論是否使用volatile關鍵字 - 所以這個關鍵字沒有如果您使用這些處理器,則會影響硬件。但是,volatile會影響編譯器和CLR所執行的優化 - 以及64位AMD和更大程度上的Itanium處理器。這意味着你不能因爲你的客戶運行特定類型的CPU而放鬆。

(即使你使用揮發性的,你還是應該保持的焦慮感健康,因爲我們很快就會看到!)」