返回任务时链接任务的正确方法是什么? [英] What is the proper way to chain Tasks when returning a Task?

查看:41
本文介绍了返回任务时链接任务的正确方法是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在C#中使用Tasks是如此,但是当我尝试从一个方法返回Task时,我感到困惑,并且该方法将在自身内部执行多个任务.那么,是否让我的方法启动一个新的Task,然后依次执行其中的所有任务?.ContinueWith()

I am so so with using Tasks in C# but I get confused when I try to return a Task from a method and that method will do multiple tasks within itself. So do I have my method spin up a new Task and then do everything sequentially inside of there? It's hard to wrap my head around doing it all with .ContinueWith()

示例:

public Task<string> GetSomeData(CancellationToken token)
{
    return Task.Factory.StartNew(() =>
    {
        token.ThrowIfCancellationRequested();

        var initialData = GetSomeInteger(token).Result;

        return GetSomeString(initialData, token).Result;
    });
}

public Task<int> GetSomeInteger(CancellationToken token)
{
    return Task<int>.Factory.StartNew(() =>
    {
        return 4;
    }, token);
}

public Task<string> GetSomeString(int value, CancellationToken token)
{
    return Task<string>.Factory.StartNew(() =>
    {
        return value.ToString();
    }, token);
}

我不确定如何再编写此方法以使其正确使用Tasks.我想我只是觉得里面应该有一个.ContinueWith之类的东西.

I am unsure how else to write this method to make it use Tasks correctly. I guess I just feel like there should be a .ContinueWith in there or something.

可能的解决办法?

public Task<string> GetSomeData(CancellationToken token)
{
    return GetSomeInteger(token).ContinueWith((prevTask) =>
    {
        return GetSomeString(prevTask.Result, token);
    }, token).Unwrap();
}

推荐答案

通常,如果您已经在使用基于任务的方法,通常最好避免重复处理新任务.链接任务而不是显式阻塞将减少系统的开销,因为它不会使ThreadPool线程一直处于等待状态.

In general, it's often best to try to avoid spinning up new tasks if you are already working with task-based methods. Chaining tasks instead of blocking explicitly will reduce the overhead of the system, as it won't keep a ThreadPool thread tied up waiting.

话虽这么说,但在执行操作时仅阻塞通常会更简单.

That being said, it's often simpler to just block as you're doing.

请注意,C#5使这一过程变得更加简单,它提供了可以同时兼顾两者的API:

Note that C# 5 makes this far simpler, providing an API that gives you the best of both:

public async Task<string> GetSomeData(CancellationToken token)
{
    token.ThrowIfCancellationRequested();

    var initialData = await SomeOtherMethodWhichReturnsTask(token);

    string result = await initialData.MethodWhichAlsoReturnsTask(token);

    return result;
};


更新后

鉴于新代码,没有一种简单的方法可以直接将其与 ContinueWith 链接起来.有两种选择.您可以使用展开来转换 Task< Task< string>> 您将创建的,即:

Given the new code, there isn't an easy way to chain this directly with ContinueWith. There are a couple of options. You can use Unwrap to convert the Task<Task<string>> you'd create, ie:

public Task<string> GetSomeData(CancellationToken token)
{
    Task<Task<string>> task = GetSomeInteger(token)
                               .ContinueWith(t => 
                               {
                                   return GetSomeString(t.Result, token);
                               }, token);
    return task.Unwrap();
}

或者,您可以使用 TaskCompletionSource< T> :

Alternatively, you can handle the unwrapping yourself elegantly with TaskCompletionSource<T>:

public Task<string> GetSomeData(CancellationToken token)
{
    var tcs = new TaskCompletionSource<string>();

    Task<int> task1 = GetSomeInteger(token);
    Task<Task<string>> task2 = task1.ContinueWith(t => GetSomeString(t.Result, token));
    task2.ContinueWith(t => tcs.SetResult(t.Result.Result));
    return tcs.Task;
}

这使整个过程可以正常工作,而无需创建新的Task(将线程池线程捆绑在一起),也不会阻塞.

This allows the entire process to work without creating a new Task (which ties up a threadpool thread), and without ever blocking.

请注意,您可能希望添加取消后的延续,并使用 tcs.也可以在请求取消时使用SetCancelled .

Note that you would probably want to add continuations on cancellation, and use tcs.SetCancelled when a cancellation was requested, as well.

这篇关于返回任务时链接任务的正确方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆