1

How can I wait for the task to start. The following code fails:

var asyncmethod = ...a Task<TReturn>, with .Start() called on it...;
int waitcounter = 0;
while (!asyncmethod.Wait(1000))
{
    waitcounter++;
    Log("waiting very long...");
}
ret = asyncmethod.Result;

The asyncmethod.Wait(1000) waits 1 seconds as expected, but the Task is in the state WaitingToRun and will never start running when Wait()ing. On the other hand, when .Result is called, it will start running. How to get it to run without calling .Result?

Edwin
  • 527
  • 7
  • 15
  • 3
    `await asyncmethod()`? – xZ6a33YaYEfmv Oct 14 '15 at 08:43
  • 1
    Please show us a minimal, complete reproduce of your problem, not pseudo-code. Why are you creating a task and immediately blocking on it with `Wait`? – Yuval Itzchakov Oct 14 '15 at 08:46
  • Why not use "Thread.Sleep(1000);" instead? – Sophie Coyne Oct 14 '15 at 08:48
  • 1
    @Rariolu because its terrible! http://stackoverflow.com/questions/8815895/why-is-thread-sleep-so-harmful – M.kazem Akhgary Oct 14 '15 at 08:51
  • await it, calling `Result` is blocking, so it will also wait for it to complete. – Jamie Rees Oct 14 '15 at 08:58
  • Do you want to know when the task transitions to running or when the task is complete? – Gusdor Oct 14 '15 at 08:59
  • You should provide more details about execution context. For example, if you use single threaded task scheduler, than it is not possible to start new task, while current one still executing. But `Task.Result` can trigger task inlining and execute task. – user4003407 Oct 14 '15 at 09:09
  • @PetSerAl does every TPL question have to tell you about the scheduler to pass this criteria? Mod down all the things. – Gusdor Oct 14 '15 at 09:11
  • @Yuval, because the task is created somewhere else in generic code and sometimes must be synced to the UI thread by waiting for it's completion. – Edwin Oct 14 '15 at 09:22
  • @Gusdor, actually I don't need to know either, I want to wait for it to start executing. Just like a call to .Result does, but like the .Wait(...) way of doing the waiting in parts, so I can do some logging while waiting. – Edwin Oct 14 '15 at 09:22
  • @Gusdor What criteria are you talking about? OP asking about scheduling task to execution. So, for me, it is reasonable to ask about execution context. Is it single threaded like UI application or not. Sorry, my English not that good, and maybe does not understand correctly, what you are asking. – user4003407 Oct 14 '15 at 09:25
  • @Edwin Does your code run in UI thread? Does task scheduled to run in UI thread? – user4003407 Oct 14 '15 at 09:28
  • @Edwin `Wait` and `Result` block until the `Task` has _completed_. – Gusdor Oct 14 '15 at 09:28
  • @PetSerAl, execution context is indeed a factor. It is generic code that sometimes gets hit on the UI thread, sometimes scheduled on another thread. Like you said, Task.Result can trigger task inlining and execute task, but apparently Wait() cannot. So what I'm looking for is a way to "wait" for the task to start/force it to start, so I can then Wait() on it in a loop and Log. I understand you cannot really wait when the code is inlined and running single threaded, but that's not a problem; the code shouldn't break, regardless of if the task is scheduled on the "current" or a separate thread. – Edwin Oct 14 '15 at 09:35
  • 1
    @Edwin `Wait()` the one, without parameters also can trigger task inlining. The problem here is, that if task is running in UI thread, than you can not interleave with it in UI thread. You can only see it in `WaitingToRun` or completed state. If you want to observe state change of task running in UI thread, then you have to put your logging in separate thread. – user4003407 Oct 14 '15 at 09:45
  • @Gusdor, that might be true, but Wait(1000) apparently doesn't force the execution of the Task, while .Result does, which is exactly the problem I'm facing. – Edwin Oct 14 '15 at 09:57
  • @PetSerAl, seems very logic. So, then, is there something like while (!asyncmethod.IsInlined || !asyncmethod.Wait(1000)) ? – Edwin Oct 14 '15 at 10:01
  • @Edwin AFAIK, TPL does not expose anything, that allow you to try to inline task by yourself. IMHO, you should really look for `async`/`await` solution for this, so you does not block UI thread and allow task to run. – user4003407 Oct 14 '15 at 11:08

4 Answers4

1

the Task is in the state WaitingToRun and will never start running when Wait()ing

When a task is in the WaitingToRun state, that means it is ready to start running and is just waiting for its scheduling context to be available, so it can be scheduled and run (as I describe on my blog).

Since the task is still in this state after Wait(1000) finishes, then presumably the task is waiting for the scheduling context that is used by the calling thread, and thus cannot be scheduled until that thread is free.

Task.Result can trigger task inlining and execute task, but apparently Wait() cannot.

Both .Result and .Wait() will permit the task to be inlined, but of course .Wait(x) cannot because it has to honor the timeout.

However, neither .Result nor .Wait() will guarantee inlining - and it's important to keep that in mind when writing reliable code.

the code shouldn't break, regardless of if the task is scheduled on the "current" or a separate thread.

That's an extremely difficult requirement to satisfy. Are you sure you need that?

The easiest solution would be to wait asynchronously:

Task<T> asyncmethod = ...;
int waitcounter = 0;
while (await Task.WhenAny(Task.Delay(1000), asyncmethod) != asyncmethod)
{
  waitcounter++;
  Log("waiting very long...");
}
ret = await asyncmethod;
Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
0

Just wait for the task to be completed using:

asyncmethod.Start();
asyncmethod.Wait();  // not needed in most cases
// but if used, the task is completed at this point.
var ret = asyncmethod.Result; // automatically waits for the task to be completed

but basically, the waiting is not neccesary, unless you have a reason for this. From the Task<TResult>.Result-docs:

The get accessor for this property ensures that the asynchronous operation is complete before returning. Once the result of the computation is available, it is stored and will be returned immediately on later calls to Result. (from msdn)

nozzleman
  • 9,529
  • 4
  • 37
  • 58
  • this will indeed always yield the result, but I would like to Wait(1000) so I can Log that it's taking a long time. But apparently Wait() isn't exactly like .Result in the way that .Result gets the Task to it's executing fase, while Wait() won't do that. e.g. you can Wait() forever on a Task that hasn't been started... – Edwin Oct 14 '15 at 09:25
  • 1
    this is true, but this would normally also mean that `.Result` would never return too. – nozzleman Oct 14 '15 at 09:35
  • is sugegst trying to rephrase your question into what you are actually trying to achieve. maybe an backgroundworker would be more suitable for your need. but that would require more information – nozzleman Oct 14 '15 at 09:40
0

Not really sure why you're doing this, but this can be achieved without blocking the calling thread using Task.IsCompleted and Task.Delay:

public async Task FooAsync()
{
    var waitCounter = -1;
    var task = Task.Run(() => { });
    do
    {
        waitCounter++;
        await Task.Delay(1000);
    }
    while (!task.IsCompleted)
}
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
0

This snippet will call Log a single time if the Task takes more than 1000ms to complete.

    private async static void StartTask()
    {
        Task<object> asyncmethod = ... ;

        LogDurationTooLong(asyncmethod, 1000);

        var result = await asyncmethod;
    }

    /// <summary>
    /// Logs if a task takes too long to complete.
    /// </summary>
    /// <param name="asyncmethod">The task to reference.</param>
    /// <param name="duration">The duration after which a log entry is made.</param>
    private async static void LogDurationTooLong(Task asyncmethod, int duration)
    {
        Task completedTask = await Task.WhenAny(Task.Delay(duration), asyncmethod);

        if (completedTask != asyncmethod)
        {
            Log("waiting very long...");
        }
    }
Gusdor
  • 14,001
  • 2
  • 52
  • 64