我正在尝试在C#中链接Task<T>
对象,就像在JavaScript中完成一样,而不会阻止UI线程。
我看到有类似问题here,但它使用非通用Task
对象作为进程函数的返回类型。我尝试对Task<T>
执行相同操作。
我也看到here对我的需求是一个更接近的问题,但是接受的答案似乎使用了.Result
两次,我想这将阻止UI线程。另外,请注意我动态链接任务,因此我无法遵循一些简单的解决方法。此外,给定here的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线程阻塞)操作)。
我希望这很简单,但我在这里看不到解决方案......难道不是有一个好的,简单的方法吗?
(注意:我想我应该在Unwrap()
之后拨打ContinueWith()
,但这似乎是一个细节......)
答案 0 :(得分:4)
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));
}
现在,您的排队任务机制与此点击处理程序需要执行的业务逻辑分离。
答案 1 :(得分:3)
最简单的方式来链接&#34;只是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。