繁体   English   中英

链接`任务 <T> 动态地并且不阻塞线程

[英]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);
}

另外,请避免StartNewContinueWith 由于它们的默认调度程序,它们是危险的API。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM