[英]Chaining `Task<T>` dynamically and without blocking the thread
我正在嘗試像在JavaScript中那樣在C#中鏈接Task<T>
對象,並且不阻塞UI線程。
我看到有一個類似的問題在這里 ,但它采用了非通用Task
對象的過程函數的返回類型。 我嘗試對Task<T>
做同樣的事情。
我也看到, 這里是我的需要仔細一問,但接受的答案似乎使用.Result
兩次,我猜會阻塞UI線程。 另外,請注意,我是動態鏈接任務的,因此無法遵循一些簡單的解決方法。 而且, 這里給出的Then
實現似乎也是同步的(我不確定是否僅更改此舊示例代碼上的TaskContinuationOptions
就可以實現我想要的功能)。
這是我現在擁有的,但是即使不阻塞線程也無法編譯它:
// Initial dummy task.
private Task<bool> taskChain = Task.Factory.StartNew<bool>(() => true);
// Chain dynamically on button click.
private async void DoSth_Click(object sender, RoutedEventArgs e)
{
var data = ....;
System.Threading.Tasks.Task<bool> del = async (t) => { return await ConvertAsync(data); };
taskChain = taskChain.ContinueWith<bool>(() => del);
var res = await taskChain;
}
我嘗試了各種不同的方法,但是我看不到如何將Task<T>
變成Func<Task<T>, T>
, ContinueWith<bool>()
似乎需要(至少沒有做一些討厭的UI線程)阻止操作)。
我希望這很容易,但是我在這里看不到解決方案……難道沒有一個簡便的方法嗎?
(注意:我想我應該在ContinueWith()
之后調用Unwrap()
,但是這似乎是一個細節……)
UnWrap
是您的朋友在這里。 它可以讓您擁有一個可以解析為Task
的延續方法,然后在延續被觸發之前獲得一個代表該任務的Task
。
還要注意, FromResult
應該用於創建已經完成的任務。
private Task<bool> taskChain = Task.FromResult(true);
private async void DoSth_Click(object sender, RoutedEventArgs e)
{
var data = CreateData();
taskChain = taskChain.ContinueWith(t => ConvertAsync(data))
.Unwrap();
var res = await taskChain;
}
請注意,我建議不要在點擊處理程序中進行此操作。 創建一個類,它是能夠排隊的任務,然后使用 。 當然,這種隊列類也遵循以下相同的模式:
public class TaskQueue
{
private Task previous = Task.FromResult(false);
private object key = new object();
public Task<T> Enqueue<T>(Func<Task<T>> taskGenerator)
{
lock (key)
{
var next = previous.ContinueWith(t => taskGenerator()).Unwrap();
previous = next;
return next;
}
}
public Task Enqueue(Func<Task> taskGenerator)
{
lock (key)
{
var next = previous.ContinueWith(t => taskGenerator()).Unwrap();
previous = next;
return next;
}
}
}
這將使您可以編寫:
private TaskQueue taskQueue = new TaskQueue();
private async void DoSth_Click(object sender, RoutedEventArgs e)
{
var data = CreateData();
var res = await TaskQueue.Enqueue(ConvertAsync(data));
}
現在,您的排隊任務機制與該單擊處理程序需要執行的業務邏輯分離。
最簡單的“連鎖”方式是await
:
// Initial dummy task.
private Task taskChain = Task.FromResult(true);
// Chain dynamically on button click.
private async void DoSth_Click(object sender, RoutedEventArgs e)
{
var data = ....;
taskChain = ChainedConvertAsync(taskChain, data);
var res = await taskChain;
...
}
private async Task<Result> ChainedConvertAsync(Task precedent, Data data)
{
await precedent;
return await ConvertAsync(data);
}
另外,請避免StartNew
和ContinueWith
; 由於它們的默認調度程序,它們是危險的API。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.