开发者

Is it safe to share local variable between threads (via a callback closure)?

开发者 https://www.devze.com 2023-04-01 07:31 出处:网络
I want to do something like the following - basically I am calling an async operation which will call a callback in another thread and I want to wait for it to complete \"inline\". My worry is that th

I want to do something like the following - basically I am calling an async operation which will call a callback in another thread and I want to wait for it to complete "inline". My worry is that that changes variables shared across threads (bar & event) may not be synchron开发者_JAVA技巧ized due to being stored in registers for example. If they were member variables I could mark them volatile but volatile can’t be used on local variables created on the stack. I could use member variables but I think its cleaner not clutter my class by keeping it all local.

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


Yes, it will work correctly. Read here

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

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

and in the signaling constructs the ManualResetEvent is included.

If you want to know what a full fence is, in the same page:

Full fences The simplest kind of memory barrier is a full memory barrier (full fence) which prevents any kind of instruction reordering or caching around that fence. Calling Thread.MemoryBarrier generates a full fence;


I think your code will work - the closure will lift then into the heap even if they were just stack-variables (the ManualReseetEvent will surely not be).

But why don't you put everything after event.WaitOne() just inside the continuation (the block were event.Set is called)? I think this should be the prefered way to handle this kind of situation and you won't run into trouble this way (you don't need the Bar in the outer block at all, and you can still use the MRE to check).

I would consider turning this into Operations using Task objects - this would solve all of this in one go (for example return a Task from your AsyncOperation). You can then wait for the result of the Task and use the returned 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;
                   });
 }
}

and use it like this:

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: The closure will basically wrap them just into a class-object ;) PPS: it's hard for me to test this code without your AsyncOperation but it should work modulo syntax errors by wrong spelling/typing I made

0

精彩评论

暂无评论...
验证码 换一张
取 消