在线程之间共享局部变量是否安全(通过回调闭包)?

时间:2011-09-02 11:04:49

标签: c# .net multithreading concurrency

我想做类似以下的事情 - 基本上我调用异步操作,它将在另一个线程中调用回调,我想等待它完成“内联”。我担心的是,由于存储在寄存器中,因此跨线程共享的变量变量(bar& event)可能无法同步。如果它们是成员变量,我可以将它们标记为volatile,但volatile不能用于堆栈上创建的局部变量。我可以使用成员变量,但我认为它更干净,不会让我的课程变得整洁。

Bar bar = null;
ManualResetEvent event = new ManualResetEvent(false);

foo.AsyncOperation(new Action(()=>{    
    // This delegate will be called in another thread
    bar = ...
    event.Set();
}));

event.WaitOne(timeout);
// use bar

2 个答案:

答案 0 :(得分:6)

是的,它会正常工作。在这里阅读

http://www.albahari.com/threading/part4.aspx

The following implicitly generate full fences: Setting and waiting on a signaling construct

并且在信令结构中包括ManualResetEvent

如果您想知道full fence是什么,请在同一页面中:

  

完全围栏最简单的内存屏障是完整内存   屏障(全栅栏),防止任何类型的指令重新排序   或围绕那个围栏缓存。调用Thread.MemoryBarrier生成一个   全围栏;

答案 1 :(得分:4)

我认为你的代码会起作用 - 即使它们只是堆栈变量,闭包也会提升到堆中(ManualReseetEvent肯定不会)。

但是为什么不把所有东西放在event.WaitOne()之后只是在continuation中(块是event.Set被调用)?我认为这应该是处理这种情况的首选方式,你不会以这种方式遇到麻烦(根本不需要外部块中的Bar,你仍然可以使用MRE来检查)。

我会考虑使用Task对象将其转换为Operations - 这可以一次性解决所有这些问题(例如从AsyncOperation返回一个Task)。然后,您可以等待任务的结果并使用返回的Bar ...

class Foo
{ 
 // ...
 private Task<Bar> AsyncOperation(Task<Bar> initializeBar)
 {
   return initializeBar
          .ContinueWith(
            bar => { 
                     /* do your work with bar and return some new or same bar */ 
                     return bar;
                   });
 }
}

并像这样使用它:

var task = foo.AsyncOperation(Taks.Factory.StartNew(() => { /* create and return a bar */ }));
var theBar = task.Result; // <- this will wait for the task to finish
// use your bar

PS:闭包基本上将它们包装成一个类对象;) PPS:我很难在没有你的AsyncOperation的情况下测试这段代码,但是我应该通过错误的拼写/输入来模拟语法错误