4

I have a method which does repeating work using a task and async/await.

    public static Task ToRepeatingWork(this Action work, int delayInMilliseconds)
    {
        Action action = async () =>
        {                
            while (true)
            {
                try
                {
                    work();
                }
                catch (MyException ex)
                {
                    // Do Nothing
                }
                await TaskEx.Delay(new TimeSpan(0, 0, 0, 0, delayInMilliseconds));
            }
        };
        return new Task(action, SomeCt, TaskCreationOptions.LongRunning);
    }

I have written a corresponding test:

    [TestMethod, TestCategory("Unit")]
    public async Task Should_do_repeating_work_and_rethrow_exceptions()
    {
        Action work = () =>
        {
            throw new Exception("Some other exception.");
        };

        var task = work.ToRepeatingWork(1);
        task.Start();
        await task;
    }

I am expecting this test to fail, but it passes (and crashes the test-runner).

However if in the ToRepeatingWork method, I change the action from async to a normal action and use Wait instead of await, the test behaves as expected.

TaskEx.Delay(new TimeSpan(0, 0, 0, 0, delayInMilliseconds)).Wait();

What is wrong here?

mybirthname
  • 17,949
  • 3
  • 31
  • 55
SharePoint Newbie
  • 5,974
  • 12
  • 62
  • 103

1 Answers1

1

You should never, ever use the task constructor. If you have work to place on the thread pool, then use Task.Run. This is a problem, but not what is causing the crash.

You should also avoid async void, so use Func<Task> instead of Action. This is what is causing the crash.

public static Task ToRepeatingWork(this Action work, int delayInMilliseconds)
{
  Func<Task> action = async () =>
  {                
    while (true)
    {
      try
      {
        work();
      }
      catch (MyException ex)
      {
        // Do Nothing
      }
      await TaskEx.Delay(new TimeSpan(0, 0, 0, 0, delayInMilliseconds));
    }
  };
  return Task.Run(() => action());
}

[TestMethod, TestCategory("Unit")]
public async Task Should_do_repeating_work_and_rethrow_exceptions()
{
  Action work = () =>
  {
    throw new Exception("Some other exception.");
  };

  var task = work.ToRepeatingWork(1);
  await task;
}
Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • is there a reason why you have not marked the lambda passed to Task.Run as async and what is the difference between this and `Task.Run(async () => await action())`? Thanks for the help – SharePoint Newbie Oct 18 '16 at 17:24
  • @SharePointNewbie: [It's the same thing](http://stackoverflow.com/questions/19098143/what-is-the-purpose-of-return-await-in-c). – Stephen Cleary Oct 18 '16 at 18:13