I'm learning the Task Parallel Library (in conjunction with C# 5.0 async
/await
), and I want to do something like this:
public class Foo
{
public void UnblockDoSomething()
{
DoWork();
// notify DoSomethingAsync
}
public async Task DoSomethingAsync()
{
DoSomeWork();
await ... // Wait until UnblockDoSomething is called
DoMoreWork();
}
}
In a traditional threaded model, I can accomplish this using condition variables. What is the TPL solution to this problem?
A conditional variable in operating system programming is a special kind of variable that is used to determine if a certain condition has been met or not. It is used to communicate between threads when certain conditions become true. A conditional variable is like a queue.
A condition variable is an explicit queue that threads can put themselves on when some state of execution (i.e., some condition) is not as desired (by waiting on the condition); some other thread, when it changes said state, can then wake one (or more) of those waiting threads and thus allow them to continue (by sig- ...
Creating and destroying condition variables A condition variable is created by calling the pthread_cond_init subroutine. You may specify a condition attributes object. If you specify a NULL pointer, the condition variable will have the default attributes.
Condition variables are synchronization primitives that enable threads to wait until a particular condition occurs. Condition variables are user-mode objects that cannot be shared across processes. Condition variables enable threads to atomically release a lock and enter the sleeping state.
If you just have a one-at-a-time notification, you can use TaskCompletionSource
:
public class Foo
{
private TaskCompletionSource<object> _signal = new TaskCompletionSource<object>();
public void UnblockDoSomething()
{
DoWork();
_signal.SetResult(null);
_signal = new TaskCompletionSource<object>();
}
public async Task DoSomethingAsync()
{
var continueSignal = _signal.Task;
DoSomeWork();
await continueSignal;
DoMoreWork();
}
}
Another option is to use a semaphore (SemaphoreSlim
), which will "remember" if it's been signalled previously:
public class Foo
{
private readonly SemaphoreSlim _mutex = new SemaphoreSlim(0);
public void UnblockDoSomething()
{
DoWork();
_mutex.Release();
}
public async Task DoSomethingAsync()
{
DoSomeWork();
await _mutex.WaitAsync();
DoMoreWork();
}
}
If you truly need a condition variable, you can use AsyncConditionVariable
from my AsyncEx library:
public class Foo
{
private readonly AsyncLock _mutex = new AsyncLock();
private readonly AsyncConditionVariable _cv = new AsyncConditionVariable(_mutex);
public void UnblockDoSomething()
{
using (await _mutex.LockAsync())
{
DoWork();
_cv.Notify();
}
}
public async Task DoSomethingAsync()
{
using (await _mutex.LockAsync())
{
DoSomeWork();
await _cv.WaitAsync();
DoMoreWork();
}
}
}
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