Running Task.Factory.StartNew with TaskScheduler.FromCurrentSynchronizationContext() will block if the delegate blocks (Thread.Sleep)

  • Thread starter Thread starter RazvanCS
  • Start date Start date
R

RazvanCS

Guest
I would like to call asynchronously from a Form (a UI Thread - MAIN_UITh) a function (F2) to run in another UI Thread - Second_UITh (another Form) having also ability to timeout. Of course - the function in the Second_UITh will not finish after the timeout occurs, but the async call will continue in MAIN_UITh. I found different implementations for the timeout ( (1) The Task-based Asynchronous Pattern Stephen Toub, Microsoft February 2012), (2) Crafting a Task.TimeoutAfter Methodby Joe Hoag. The idea is to:

  • launch async the function (Func<TResult>) using the only method that allows to use the TaskScheduler.FromCurrentSynchronizationContext() of the Second_UITh which is Task.Factory.StartNew. Based on the Startnew is dangerous by Stephen Cleary my case represents a good reason to use the "dangerous" function
var workingTask = Task.Factory.StartNew<int>(
() => F2(p1,p2) ,
combinedCTS.Token,
TaskCreationOptions.DenyChildAttach,
TaskSchedulerForm);

  • await for any of the two task:
var completedTask = await Task.WhenAny(workingTask, Task.Delay(nTimeoutMs));

if (completedTask == workingTask)
{
// workingTask done in time
// return the result of the workingTask
}
else
{
//timeout
}
The problem that I have is that if F2(...) function is blocking (simulated with a Thread.Sleep) the whole step 1 Task.Factory.StartNew

is blocking and I cannot start the awaiting for the Timeout (step 2).

If F2(...) is not blocking then everything runs as expected.

Details:

- the TaskSchedulerForm variable is initialized in the constructor of the second Form (that runs in Second_UITh)

TaskSchedulerForm = TaskScheduler.FromCurrentSynchronizationContext();

- The function F2(...) runs in Second_UITh. But it has to finish and only after that the second step is able to run

- The synchronous part (first part before the Task.Factory.StartNew<int>) runs as expected in the MAIN_UITh. Since I don't have a await in the call of Task.Factory.StartNew<int> and F2(...) runs (or it is scheduled) to run on a different thread, I expect the code of second step (await Task.WhenAny) to execute before the F2(...) finishes.

- if I remove the Sleep from F2(...) then it is working as expected.

- The F2(...) can be a function from a third party library which not implement (or at least not in a consistent manner) a timeout feature. It involves some socket communication but I don't know the details (it is not a full CPU-bound task). The library can be an ACTIVEX component - but in my test case F2 is just a simply blocking function.

- I tried also a make F2 async. (and the the StartNew becomes <Task<int>> with all the Unwrap() spill)


Thanks,

Razvan

Continue reading...
 
Back
Top