簡體   English   中英

如何等待稍后啟動的任務

[英]How to wait for later started task

在我的代碼示例中,當 task2 完成時,主線程不會等待。

public async Task Run()
{
    Console.WriteLine("Main start");
    await getTask1();
    Console.WriteLine("Main 2");
    var task2 = getTask2();
    await Task.Delay(1);
    Console.WriteLine("Main 3");
    task2.Start();
    await task2;
    Console.WriteLine("Main end");
}

private async Task getTask1()
{
    Console.WriteLine("task1 start");
    await Task.Delay(100);
    Console.WriteLine("task1 end");
}

private Task getTask2()
{
    return new Task(async () =>
    {
        Console.WriteLine("task2 start");
        await Task.Delay(100);
        Console.WriteLine("task2 end");
    });
}

這段代碼的執行結果是

主要開始
任務 1 開始
任務 1 結束
主要 2
主要 3
任務 2 開始
主端
任務2結束

如何更改“ Main end ”將位於列表末尾的代碼。

getTask2你開始一個Task ,只是啟動另一個Task 因此,當您awaitTask時,它將在完成啟動內部任務后立即繼續執行,而不是在該內部任務完成后繼續執行。

在這種情況下,您根本沒有理由創建一個新任務只是為了開始一個新任務; 您應該只使用第一種方法中使用的方法。

當然,如果出於某種原因,您確實需要創建一個新Task來啟動新任務,那么您需要取出內部Task並確保 main 方法僅在該內部Task完成后繼續執行。 有一個Unwrap方法來創建一個Task ,它在邏輯上等同於一個Task ,它是另一個TaskResult

private Task getTask2()
{
    Task<Task> task = new Task<Task>(async () =>
    {
        Console.WriteLine("task2 start");
        await Task.Delay(100);
        Console.WriteLine("task2 end");
    });
    task.Start();
    return task.Unwrap();
}

但你甚至不需要這樣做。 如果給定一個生成Task的 lambda, Task.Run將自動為您解開該值:

private Task getTask2()
{
    return Task.Run(async () =>
    {
        Console.WriteLine("task2 start");
        await Task.Delay(100);
        Console.WriteLine("task2 end");
    });
}

再一次,這一切都假設剛開始的異步操作需要卸載到另一個線程,這種情況應該很少見。 通常這意味着該異步方法存在錯誤,因為它不應該在首先為您提供Task之前執行長時間運行的同步工作,但有時該方法將來自您無法控制的外部庫,所以這將是您唯一的選擇。

您正在使用的Task的構造函數重載具有以下簽名:

public Task(Action action)

盡管您可以將以下表達式分配給Action類型的變量:

async () =>
    {
        Console.WriteLine("task2 start");
        await Task.Delay(100);
        Console.WriteLine("task2 end");
    }

這個動作將指向一個同步函數,將同步完成一旦awaitawait Task.Delay(100); 達到(盡管“動作”的其余部分將異步繼續)。 所以,就Task對象而言,一旦達到這個await ,這個Action的執行就完成了。 這意味着awaitawait task2; 將在該點異步繼續,這解釋了為什么在“task2 end”之前打印“Main end”。

所以基本上, Task的構造函數不支持異步操作,只支持同步操作。

要解決此問題,您需要使用理解異步操作的方法啟動任務。 異步操作如下所示: Func<Task>

Task.Run方法支持異步操作,因為它具有以下重載:

public static Task Run(Func<Task> function)

因此,要修復您的代碼,請將return new Task(...更改為return Task.Run(...然后不要在返回的任務上調用Start方法,因為Task.Run將創建一個已經啟動的Task

如果您想延遲第二個“任務”的執行,那么我建議讓getTask2方法返回Func<Task> (異步操作)而不是像這樣的Task

private Func<Task> getTask2()
{
    return async () =>
    {
        Console.WriteLine("task2 start");
        await Task.Delay(100);
        Console.WriteLine("task2 end");
    };
}

然后在需要時從Run方法運行這樣的異步操作,如下所示:

public async Task Run()
{
    Console.WriteLine("Main start");
    await getTask1();
    Console.WriteLine("Main 2");
    var task2 = getTask2(); //construct asynchronous action
    Console.WriteLine("Main 3");
    await Task.Run(task2); //execute the asynchronous action
    Console.WriteLine("Main end");
}

new Task(本身不適用於異步函數。您正在創建一個Task<Task> ,您需要在等待內部任務之前調用Unwrap()

private Task<Task> getTask2()
{
    return new Task(async () =>
    {
        Console.WriteLine("task2 start");
        await Task.Delay(100);
        Console.WriteLine("task2 end");
    });
}

var task2 = getTask2();
Console.WriteLine("Main 3");
task2.Start();
await task2.Unwrap();

您的代碼中有錯誤。 您尚未使用關鍵字async標記getTask2方法,但您正在await其結果。 即使您在方法中使用async委托,如果要成功等待它,您也必須使用async關鍵字。

這是關於如何實現所需結果的想法。

public async Task Run()
    {
        Console.WriteLine("Main start");
        await getTask1();
        await getTask2();
        Console.WriteLine("Main end");
    }

    public async Task AlternateRun()
    {
        Console.WriteLine("Main start");
        List<Task> task_list = new List<Task>();
        task_list.Add(getTask1());
        task_list.Add(getTask2());

        var task_result = Task.WhenAll(task_list);
        task_result.Wait();
        Console.WriteLine("Main end");
    }

    private async Task getTask1()
    {
        Console.WriteLine("task1 start");
        await Task.Delay(100);
        Console.WriteLine("task1 end");
    }

    private async Task getTask2()
    {
        Console.WriteLine("task2 start");
        await Task.Delay(100);
        Console.WriteLine("task2 end");
    }

我還添加了一個 AlternateRun 方法,它將演示等待多個任務的替代風格。 如果您需要按順序完成任務,請考慮使用ContinueWith擴展方法。

請注意,我還刪除了您的task2.Start()語句。 這已經由await關鍵字暗示了。

希望這可以幫助。

暫無
暫無

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

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