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

本文关键字:闭包 回调 是否 安全 之间 线程 共享 局部变量 | 更新日期: 2023-09-27 18:07:24

我想做下面的事情-基本上我正在调用一个异步操作,它将在另一个线程中调用回调,我想等待它完成"内联"。我担心的是,这会改变线程间共享的变量(bar &事件)可能由于存储在寄存器中而无法同步。如果它们是成员变量,我可以将它们标记为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

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

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

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

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

,在信令结构中包含ManualResetEvent

如果你想知道什么是full fence,在同一页:

最简单的一种内存屏障是全内存阻止任何指令重新排序的屏障(全栅栏)或者在栅栏附近缓存。调用线程。MemoryBarrier生成一个完整的栅栏;

我认为你的代码将工作-闭包将提升然后到堆,即使他们只是堆栈变量(ManualReseetEvent肯定不会)。

但是为什么不把event. waitone()之后的所有内容都放在continuation (block were event)中呢?集合叫)?我认为这应该是处理这种情况的首选方法,这样您就不会遇到麻烦(您根本不需要外部块中的Bar,并且您仍然可以使用MRE进行检查)。

我会考虑将其转换为使用任务对象的操作-这将一次性解决所有这些问题(例如从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的情况下测试这段代码,但它应该可以通过错误的拼写/输入来消除语法错误,我做了