I need some help with TPL and Tasks
Here are my scenarios:
-
I have a few event handlers that spawn tasks. If an event is invoked before its previous task is completed, I want to wait (block) it before proceeding.
-
I have a synchronous method that, when invoked, must wait until any spawned tasks from any event handlers are done, before proceeding.
public void DoSomething()
{
// Expects any running tasks from OnEvent1(), OnEvent2(), OnEvent3()
// to be completed before proceeding.
}
public void OnEvent1()
{
Task.Factory
.StartNew(()=>{ /*Long running task*/ })
.ContinueWith(task=>{ /* Updates UI */ });
}
public void OnEvent2()
{
Task.Factory
.StartNew(()=>{ /*Long running task*/ })
.ContinueWith(task=>{ /* Updates UI */ });
}
public void OnEvent3()
{
Task.Factory
.StartNew(()=>{ /*Long running task*/ })
.ContinueWith(task=>{ /* Updates UI */ });
}
An actual scenario would be:
- OnFetchData() => Spawns task. All subsequent calls to this needs to be queued.
-
OnSyncSettings() => Spawns task. All subsequent calls to this needs to be queued.
-
OnAutoBackup() => Synchronous method. Wait for any other tasks (e.g, Fetch data/Sync settings) to complete before saving.
- OnFullBackup() => Synchronous method. Manually runs FetchData() and SyncSettings(), waits for completion, proceed.
My question is simply: How can I do this?
Would this approach be correct?
-
Each event handler remembers its last . When invoked, it’ll wait for all tasks in its list to complete before proceeding.
-
For the synchronous method, it needs to wait for all tasks (From every event handler) to
Task _lastEvent1Task;
public void OnEvent1()
{
// Wait for all tasks to complete before proceeding
if (_lastEvent1Task!=null)
_lastEvent1Task.Clear();
// Spawns new task
_lastEvent1Task = Task.Factory.StartNew(()=>{ });
}
public void OnEvent3()
{
// Wait for any running tasks to complete before proceeding
if (_lastEvent1Task != null) _lastEvent1Task.Wait();
if (_lastEvent2Task != null) _lastEvent2Task.Wait();
...
// Proceed
}
Thanks!
[Edit]
When DoSomething() is waiting and Event1 is raised?
What if DoSomething() is running and an event is raised?
OnEvents()
- If first task: Spawns task and execute asynchronously
- If previous task still running, queue/block. Critical section.
- Assume:
- These events typically not raised multiple times.
- The UI is expected to prevent user from raising the same event while still running.
DoSomething() (when called)
- Assume: Event notifications are disabled.
- Assume: No new events will be queued.
- Expected: to wait until all remaining queued tasks are completed before proceeding
- Expected: to be a synchronous call, and does not return to caller until executed through
The approach you describe has concurrency issues. For example, right after the null check on
_lastEvent1Task, some other thread may set_lastEvent1Taskto null, causing aNullReferenceException.I’m assuming that every task should run synchronously but some should not be blocking. The best way to achieve this is by using a
TaskSchedulerthat utilizes just a single thread, you can find an example here.For the non-blocking methods you then get something like this:
For the blocking methods you get:
When you schedule tasks, the task scheduler will use just one thread for scheduling them, guaranteeing that all tasks run synchronously.