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 synchronized 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
Tip: Unlike class and instance field variables, threads cannot share local variables and parameters. The reason: Local variables and parameters allocate on a thread's method-call stack. As a result, each thread receives its own copy of those variables.
Each process has its own global state. Can global variables be accessed from another thread? Yes, a thread who lives in the same process can access a global variable, but you must ensure the safety of any memory that is accessed by multiple threads.
We can share a local variable between threads using a queue. The queue must be shared and accessible to each thread and within the function where the local variable is defined and used. For example, we can first create a queue. Queue instance.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With