Task.ContinueWith method requires task argument?
Asked Answered
R

2

10

I have a class with two methods, Load() and Process(). I want to be able to run these individually as background tasks, or in sequence. I like the ContinueWith() syntax, but I'm not able to get it to work. I have to take a Task parameter on the method I continue with and cannot have a Task parameter on the initial method.

I would like to do it without lambda expressions, but am I stuck either using them, forcing a task parameter on one of the methods, or creating a third method LoadAndProcess()?

void Run()
{
    // doesn't work, but I'd like to do it
    //Task.Factory.StartNew(MethodNoArguments).ContinueWith(MethodNoArguments);

    Console.WriteLine("ContinueWith");
    Task.Factory.StartNew(MethodNoArguments).ContinueWith(MethodWithTaskArgument).Wait();

    Console.WriteLine("Lambda");
    Task.Factory.StartNew(() => { MethodNoArguments(); MethodNoArguments(); }).Wait();

    Console.WriteLine("ContinueWith Lambda");
    Task.Factory.StartNew(MethodNoArguments).ContinueWith(x => { 
            MethodNoArguments(); 
        }).Wait();
}

void MethodNoArguments()
{
    Console.WriteLine("MethodNoArguments()");
}

void MethodWithTaskArgument(Task t = null)
{
    Console.WriteLine("MethodWithTaskArgument()");
}
Racial answered 17/5, 2012 at 3:17 Comment(5)
Could you describe why you do not want to use lambda expressions?Arawakan
I don't have a real reason other than I like the syntax of ContinueWith and think the lambda expression makes it less look dirtier and a tad more difficult to understand.Racial
Does Load and Process depend on each other, sequentially? Is data shared between them where thread synchronization could be trouble?Arawakan
OTOH, have you considered writing your own ContinueWith(this Task, Action) extension method?Arawakan
Bear in mind also that the continuation receives a Task as a parameter so that it can do exception handling. For an approach that combines both the syntax you want and ensures exceptions are propagated through the task sequence and don't go unhandled, take a look at the Then method that Stephen Toub builds in this post.Lifer
E
19

In all overloads of ContinueWith(), the first parameter is a delegate that takes a Task, so you can't pass a parameterless delegate to it.

I think that using lambdas is perfectly fine and that it doesn't hurt readability. And the lambda in your code is unnecessarily verbose:

Task.Factory.StartNew(MethodNoArguments).ContinueWith(_ => MethodNoArguments())

Or, as Cory Carson pointed out in a comment, you could write an extension method:

public static Task ContinueWith(this Task task, Action action)
{
    return task.ContinueWith(_ => action());
}
En answered 17/5, 2012 at 10:2 Comment(0)
N
5

Writing clean code when you use multiple continuations is not that easy, although you can follow a few rules to make code cleaner:

  1. Use the shorter form from svick's answer
  2. Avoid chaining the continuations. Store the result of each continuation in a separate variable and then call ContinueWith in a separate line
  3. If the continuation code is long, store it in a lambda variable and then ContinueWith the lambda.
  4. Use an extension method like this "Then" method to make your code even cleaner.

You can change your code to something like this, which is slightly cleaner:

        var call1=Task.Factory.StartNew(()=>MethodNoArguments());
        var call2 = call1.ContinueWith(_ => MethodNoArguments());
        call2.Wait();

or even

        var call1 = Task.Factory.StartNew<Task>(MethodNoArguments);
        var call2 = call1.Then(MethodNoArguments);
        call2.Wait();

Stephen Toub discusses the Then extension and other ways you can clean up your code in Processing Sequences of Asynchronous Operations with Tasks

This problem is solved for good in C# 4. In C# 5 you can use the async/await keywords to create clean code that looks like the original synchronous version, eg:

    static async Task Run()
    {
        await MethodNoArguments();
        await MethodNoArguments();
    }

    static async Task MethodNoArguments()
    {
        await Task.Run(()=>Console.WriteLine("MethodNoArguments()"));
    }

Visual Studio 11 and .NET 4.5 have a Go Live license so you could probably start using them right away.

You can use the Async CTP in C# 4 to achieve the same result. You can use the Async CTP in production as it has a go live license. The downside is that you 'll have to make some small changes to your code when you move to .NET 4.5 due to differences between the CTP and .NET 4.5 (eg. CTP has TaskEx.Run instead of Task.Run).

Narda answered 17/5, 2012 at 11:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.