簡體   English   中英

有沒有辦法使用ContinueWith任務啟動任務?

[英]Is there any way to start task using ContinueWith task?

我的代碼:

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();   

要么

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

例外:

可能不會在繼續任務上調用Start。

所以我需要開始第一項任務。 有沒有辦法調用上一個任務Start方法來運行所有任務?

有什么理由不使用Task.Factory.StartNew msdnms docs作第一個任務? 是的,它是不一致的 - 但它從根本上說是一種不同的任務,就明確而不僅僅是一種延續而言。

我真的不確定寫這個是什么問題:

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();

或這個:

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

這直接表達了你真正想做的事情。 (這是你想要開始的初始任務。那么在初始任務上調用Start會有什么問題?)為什么你要找一個模糊它的語法?

編輯 :修復第一個例子......

編輯2添加:

所以我現在意識到LinqToTasks希望任務選擇器返回正在運行的任務。 因此from第一個示例中的第二個from子句返回一個任務都from運行的任務。 所以你真正需要的是:

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();

沒有其他任何東西可以在這些from子句中生成的任務上調用Start 由於相關選擇器在前一個任務完成之前實際上並未執行,因此您仍然可以控制何時啟動根任務。

這似乎有效,但它非常難看。 但這似乎是LinqToTasks的設計方式......我想我會堅持使用常規函數調用語法。

我今天遇到了同樣的問題。 我想創建一個處理內部任務錯誤的包裝器任務。 這就是我想出的:

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;

這里的關鍵特性是 - 延續部分在原始任務之后運行,如您所願 - 包裝器是可啟動的。 使用ContineWith()創建的繼續任務是nto ContineWith()

此示例的一個不太重要的特性是,異常被記錄並丟棄(解決了我的問題,而不是你的問題)。 當延續中發生異常時,您可能希望做一些不同的事情,例如將其重新拋出作為當前任務的例外,以便它冒出來。

據我所知,沒有合理的方法來組成框架提供的非啟動任務。 我能想到的最簡單的解決方案是擴展方法。 如果您需要此功能,可以使用以下示例。

警告 :就像繞過並組成大量的lambda一樣,如果你發現自己需要這些,這通常意味着你在設計中缺少一種可以簡化代碼的類型。 通過創建子任務來問問自己獲得了什么。

/// <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);
            });
    }
}

答案很簡單。 ContinueWith自動啟動任務。 第一項任務需要運行。

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

ContinueWith返回任務,從檢查上一個任務開始是否完成。 此代碼的工作方式與下面的代碼相同

 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();
        } );

因此,如果第一個任務未完成,則不會啟動下一個任務。

你不需要開始繼續阻止。

問題是用LINQ選擇任務只會創建一個表達式樹!

所以這就是你需要做的:

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!!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM