Let's suppose I have this simple snippet:
async void button_Click(object sender, RoutedEventArgs e)
{
await Task.Factory.StartNew(() =>
{
Console.WriteLine("start");
Thread.Sleep(5000);
Console.WriteLine("end");
});
}
Obviously, everytime I push that button a new task is started even when a previous task still runs. How would I postpone any new task until all previous tasks have finished?
Some more details:
In the example above, each new task is identical to the task before. However, in the original context the sequence of tasks matters: Parameters may change (I could "simulate" it by using DateTime.Now.Ticks
).
The tasks should be executed in the order they are "registered". Specificly, my program will talk to a serial device. I've done this before with a background thread utilizing a BlockingCollection
. However, this time there's a strict request/response-protocol and I'd like to use async/await if it is possible.
Possible solution:
I could imagine creating tasks and storing them in a list. But how would I execute the tasks with respect to the requirements? Or should I return to the thread-based solution I have used before?
What about trying the
Dataflow.ActionBlock<T>
with the (default) max degree of parallelism of 1. This way you don't need to worry about any of the thread safety / locking concerns.It could look something like:
You could also setup the ActionBlock to receive a
Task
orFunc<Task>
, and simply run / await this input. Which would allow multiple operations to be queued and awaited from different sources.I might be missing something, but I don't think
SemaphoreSlim
is needed for the OP's scenario. I'd do it the following way. Basically, the code justawait
the previous pending instance of the task before continuing (no exception handling for clarity):[UPDATE] To address the comment, here's a thread-safe version, when
button_Click
can be called concurrently:You could wait on a
SemaphoreSlim
asynchronously and release it once the job is done. Don't forget to configure the semaphore initialcount to1
.I recommend using a
SemaphoreSlim
for synchronization. However, you want to avoidTask.Factory.StartNew
(as I explain on my blog), and also definitely avoidasync void
(as I explain in the MSDN article).