2016-03-07 76 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而放松。

(即使你使用挥发性的,你还是应该保持的焦虑感健康,因为我们很快就会看到!)”