[英]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.