开发者

Is there any way to start task using ContinueWith task?

开发者 https://www.devze.com 2023-01-30 11:09 出处:网络
My code: var r = from x in new Task<int>(() => 1) from y in new Task<int>(() => x + 1)

My code:

var r = from x in new Task<int>(() => 1)
        from y in new Task<int>(() => x + 1) 
        select y;
r.ContinueWith(x => Console.WriteLine(x.Result)).Start();   

or

new 开发者_JS百科Task<int>(() => 1)
    .ContinueWith(x => x.Result + 1)
    .ContinueWith(x => Console.WriteLine(x.Result))
    .Start();

Exception:

Start may not be called on a continuation task.

So I need to start the first task. Is there any way to call last task Start method to run all tasks?


Any reason not to use Task.Factory.StartNewmsdn, ms docs for the first task? Yes, it's inconsistent - but it's fundamentally a different kind of task, in terms of being started explicitly rather than just as a continuation.


I'm not really sure what's wrong with just writing this:

var t1 = new Task<int>(() => 1)
var r = from x in t1
        from y in new Task<int>(() => x + 1) 
        select y;
r.ContinueWith(x => Console.WriteLine(x.Result));
t1.Start();

or this:

var t = new Task<int>(() => 1)
t.ContinueWith(x => x.Result + 1)
 .ContinueWith(x => Console.WriteLine(x.Result))
t.Start();

That directly expresses what you actually want to do. (It's the initial task that you want to kick off. So what's wrong with invoking Start on that initial task?) Why are you looking for a syntax that obscures that?

EDIT: fixed first example...

EDIT 2 to add:

So I realise now that LinqToTasks expects task selectors to return running tasks. So the second from clause in your first example returns a task that nothing will ever run. So what you actually need is this:

var t1 = new Task<int>(() => 1);
var r = from x in t1
        from y in Task<int>.Factory.StartNew(() => x + 1)
        select y;
r.ContinueWith(x => Console.WriteLine(x.Result));
t1.Start();

Nothing else is going to call Start on the tasks produced in these from clauses. Since the relevant selectors don't actually get executed until the previous task completes, you're still in control of when to kick off the root task.

That appears to work, but it's pretty ugly. But that appears to be how LinqToTasks is designed... I think I'd stick with the regular function call syntax.


The problem is that selecting tasks with LINQ Will Only Create an Expression Tree!

So here's what you need to do:

var query = 
    from i in Enumerable.Range(1, 4) 
    let task = Task.Factory.StartNew(() => Tuple.Create(i, IsPrime(i))) // put a breakpoint here
    select task.ContinueWith(delegate {
        Console.WriteLine("{0} {1} prime.", _.Result.Item1, _.Result.Item2 ? "is" : "is not");
    });
// breakpoint never hit yet
query.ToArray(); // breakpoint hit here 4 times
// all tasks are now running and continuations will start
TaskEx.Await(query.ToArray()); // breakpoint hit 4 more times!!


I had the same problem today. I wanted to create a wrapper task that handles an error from an inner task. This is what I came up with:

var yourInitialTask = new Task(delegate
{
    throw e;
});

var continuation = task.ContinueWith(t =>
{
    if (task.IsCanceled)
    {
        Debug.WriteLine("IsCanceled: " + job.GetType());
    }
    else if (task.IsFaulted)
    {
        Debug.WriteLine("IsFaulted: " + job.GetType());
    }
    else if (task.IsCompleted)
    {
        Debug.WriteLine("IsCompleted: " + job.GetType());
    }
}, TaskContinuationOptions.ExecuteSynchronously); //or consider removing execute synchronously if your continuation task is going to take long

var wrapper = new Task(() =>
{
   task.Start();
   continuation.Wait();
});

return wrapper;

The key features here are that -the continuation part runs after the original task, as you want -the wrapper is Startable. Continuation tasks created with ContineWith() are nto Startable.

A less key feature of this example, is that the exception is being logged and discarded (solves my problem, not yours). You might want to be doing something different when exceptions occur in the continuation such as rethrow it as an exception of the current task, so that it bubbles out.


As far as I'm aware, there's no sensible way to compose non-started tasks provided by the framework. The simplest solution I can think of is extension methods. Here are some examples which you could build on if you need this functionality.

Warning: Just as with passing around and composing tons of lambdas, if you find yourself needing these, it often means you are missing a type in your design that would simplify your code. Ask yourself what you gained by creating the subtasks.

/// <summary>
/// Compose tasks without starting them.
/// Waiting on the returned task waits for both components to complete.
/// An exception in the first task will stop the second task running.
/// </summary>
public static class TaskExtensions
{
    public static Task FollowedBy(this Task first, Task second)
    {
        return FollowedBy(first,
            () =>
            {
                second.Start();
                second.Wait();
            });
    }

    public static Task FollowedBy(this Task first, Action second)
    {
        return new Task(
            () =>
            {
                if (first.Status == TaskStatus.Created) first.Start();
                first.Wait();
                second();
            });
    }

    public static Task FollowedBy<T>(this Task first, Task<T> second)
    {
        return new Task<T>(
            () =>
            {
                if (first.Status == TaskStatus.Created) first.Start();
                first.Wait();
                second.Start();
                return second.Result;
            });
    }

    public static Task FollowedBy<T>(this Task<T> first, Action<T> second)
    {
        return new Task(
            () =>
            {
                if (first.Status == TaskStatus.Created) first.Start();
                var firstResult = first.Result;
                second(firstResult);
            });
    }

    public static Task<TSecond> FollowedBy<TFirst, TSecond>(this Task<TFirst> first, Func<TFirst, TSecond> second)
    {
        return new Task<TSecond>(
            () =>
            {
                if (first.Status == TaskStatus.Created) first.Start();
                return second(first.Result);
            });
    }
}


The answer is simple. ContinueWith is automatically start task. And first task need to be running.

var r= Task<int>.Run<int>( () => 1 )
             .ContinueWith<int>( x => x.Result + 1 )
             .ContinueWith( x => Console.WriteLine( x.Result ) );

ContinueWith return task that start with checking previous task is done or not. This code work in the same way like below code

 var firstTask = new Task<int>( () => 1 );
        firstTask.Start();
        var firstawaiter = firstTask.GetAwaiter();
        var secondTask = new Task<int>( x => (int)x + 1 , firstawaiter.GetResult());

        firstawaiter.OnCompleted( () =>
        {
            secondTask.Start();
        } );

        var secondawaiter = secondTask.GetAwaiter();


        var thirdTask = new Task( x => Console.WriteLine( x ) , secondawaiter.GetResult());

        secondawaiter.OnCompleted( () =>
        {
            thirdTask.Start();
        } );

So, If first task is not completed, next task will not be started.

And you not need to start continuewith block .

0

精彩评论

暂无评论...
验证码 换一张
取 消