Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create an Awaitable Cold Task

I have an async method after the completion of which I wish to run another method. This works fine if I simply call the method and add .ContinueWith()

However, I have a new requirement which is to only start the task if I am able to add it to a concurrent dictionary.

I wish to construct the task, attempt to add it and then start the task

However, it seems that Task.Start() immediately completes the task causing the continue action to run and any waits to.. not wait.

can anyone explain why this happens and the correct way to achieve my goal?

namespace UnitTestProject2
{
    [TestClass]
    public class taskProblem
    {
        [TestMethod]
        public void Test()
        {
            CancellationTokenSource cancel = new CancellationTokenSource();
            ConcurrentDictionary<Guid, Task> tasks = new ConcurrentDictionary<Guid,Task>();
            Guid id = Guid.NewGuid();
            Task t = new Task(async () => await Get(), cancel.Token);
            t.ContinueWith(Complete);
            if (tasks.TryAdd(id, t))
            {
                t.Start();
            }
            else
            {
                //another thread is stopping stuff dont start new tasks
            }

            t.Wait(); //expected to wait for the get function to complete
            Console.WriteLine("end test");
        }

        public async Task Get()
        {
            Console.WriteLine("start task");
            await Task.Delay(10000);
            Console.WriteLine("end task");
        }

        public void Complete(Task t)
        {
            Console.WriteLine("Complete");
        }
    }
}

output:

start task
end test
Complete

expected output:

start task
end task
Complete
end test

Update: It appears there is no way to Create a new Task which won't immediately start or complete immediately on Task.Start?

like image 443
Ewan Avatar asked Apr 23 '15 13:04

Ewan


People also ask

How do I create a new task in C#?

To start a task in C#, follow any of the below given ways. Use a delegate to start a task. Task t = new Task(delegate { PrintMessage(); }); t. Start();

Does await Block C#?

When the await operator is applied to the operand that represents an already completed operation, it returns the result of the operation immediately without suspension of the enclosing method. The await operator doesn't block the thread that evaluates the async method.

What is async await and task in C#?

The async keyword turns a method into an async method, which allows you to use the await keyword in its body. When the await keyword is applied, it suspends the calling method and yields control back to its caller until the awaited task is complete. await can only be used inside an async method.


1 Answers

Your delegate is async void. async void-methods are fire and forget.

See first point of Summary of Patterns and Anti-Patterns: http://rarcher.azurewebsites.net/Post/PostContent/31

Maybe you can do something like that:

[TestFixture]
public class FIXTURENAMETests {
  [Test]
  public async Task NAME() {
    var tcs = new TaskCompletionSource<bool>();
    Task t = LongRunningStuff(tcs);

    if (CanInsertInDictionary(t)) {
      tcs.SetResult(true);
    } else {
      tcs.SetException(new Exception());
    }

    Trace.WriteLine("waiting for end");

    try {
      await t;
    }
    catch (Exception exception) {
      Trace.WriteLine(exception);
    }

    Trace.WriteLine("end all");
  }

  private bool CanInsertInDictionary(Task task) {
    return true;
  }

  private async Task LongRunningStuff(TaskCompletionSource<bool> tcs) {
    Trace.WriteLine("start");
    try {
      await tcs.Task;
    }
    catch (Exception) {
      return;
    }
    Trace.WriteLine("do long running stuff");
    await Task.Delay(10000);
    Trace.WriteLine("end");
  }
}
like image 121
Peter Punzenberger Avatar answered Oct 05 '22 04:10

Peter Punzenberger