[英]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
。 因此,當您await
該Task
時,它將在完成啟動內部任務后立即繼續執行,而不是在該內部任務完成后繼續執行。
在這種情況下,您根本沒有理由創建一個新任務只是為了開始一個新任務; 您應該只使用第一種方法中使用的方法。
當然,如果出於某種原因,您確實需要創建一個新Task
來啟動新任務,那么您需要取出內部Task
並確保 main 方法僅在該內部Task
完成后繼續執行。 有一個Unwrap
方法來創建一個Task
,它在邏輯上等同於一個Task
,它是另一個Task
的Result
:
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");
}
這個動作將指向一個同步函數,將同步完成一旦await
在await Task.Delay(100);
達到(盡管“動作”的其余部分將異步繼續)。 所以,就Task
對象而言,一旦達到這個await
,這個Action
的執行就完成了。 這意味着await
在await 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.