Reputation: 20464
I declared a method which should run a Task
asynchronously, then, when the Task
finishes, run the specified delegate synchronously.
C# example:
private void WaitUntilLoadedAsync(Process p, Action callback, int timeout = 1500)
{
Task.Factory.StartNew(() => ProcessUtil.WaitUntilLoaded(p, timeout)).
ContinueWith(() => callback.Invoke()).RunSynchronously();
}
VB.Net Example:
Private Sub WaitUntilLoadedAsync(ByVal p As Process,
ByVal callback As Action,
Optional ByVal timeout As Integer = 1500)
Task.Factory.StartNew(Sub() ProcessUtil.WaitUntilLoaded(p, timeout)).
ContinueWith(Sub() callback.Invoke()).RunSynchronously()
End Sub
However, the RunSynchronously
method throws a System.InvalidOperationException
exception teling me that a continuation Task
cannot be ran synchronouslly.
Then, how I could do it?.
Note(s):
I'm able to support Async
/Await
solutions.
I will preserve the "tiny" logic or cyclomatic complexity of my method, I mean, it is just a method on which I pass an Acton
, no delegates are declared outside the method or other complex things that could cause the end-user to write more than an Action
. The method itself should automate all the required operations to perform that continuation task synchronouslly, no complexity outside.
Upvotes: 2
Views: 4080
Reputation: 27861
So, as I understand from the question and the comments, you want to run Task 1 on a thread-pool thread (background thread) and you want to run Task 2 (which is a continuation of Task 1) on the UI thread.
Here is how you can do it:
private void WaitUntilLoadedAsync(Process p, Action callback, int timeout = 1500)
{
var thread_scheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() => ProcessUtil.WaitUntilLoaded(p, timeout)).
ContinueWith(t => callback.Invoke(), CancellationToken.None, TaskContinuationOptions.None,
thread_scheduler);
}
What this code is doing is that it asks the TPL to run the continuation task on a scheduler that will execute tasks in the UI thread.
This assumes that WaitUntilLoadedAsync
is called from the UI thread. If this is not the case, simply make thread_schedular
an instance variable (or in some scope that is reachable from this method), and make sure that you initialize it from the UI thread.
Please note that the control will return to the caller of WaitUntilLoadedAsync
immediately, and the callback will be executed on the UI thread after Task 1 is finished.
If you want to execute the continuation task on the UI thread only if WaitUntilLoadedAsync
is invoked from a UI thread (and execute it on a thread-pool thread otherwise), then define thread_scheduler
as the following:
var thread_scheduler = SynchronizationContext.Current == null
? TaskScheduler.Default
: TaskScheduler.FromCurrentSynchronizationContext();
Upvotes: 2
Reputation: 635
Sounds like you are looking for TaskContinuationsOptions.ExecuteSynchronously
Per MSDN:
Specifies that the continuation task should be executed synchronously. With this option specified, the continuation runs on the same thread that causes the antecedent task to transition into its final state. If the antecedent is already complete when the continuation is created, the continuation will run on the thread that creates the continuation. If the antecedent's CancellationTokenSource is disposed in a finally block (Finally in Visual Basic), a continuation with this option will run in that finally block. Only very short-running continuations should be executed synchronously.
Because the task executes synchronously, there is no need to call a method such as Task.Wait to ensure that the calling thread waits for the task to complete.
To use it just pass it as a flag using the overload .ContinueWith(Action<task>, TaskContinuationOptions)
However this won't be synchronous if the thread where the parent executed is aborted (which is uncommon).
Upvotes: 3