c# - 返回任务时链接任务的正确方法是什么?

标签 c# task-parallel-library

我非常喜欢在 C# 中使用 Tasks,但是当我尝试从方法返回 Task 并且该方法将在其自身内执行多个任务时,我感到困惑。那么我是否让我的方法启动一个新任务,然后在其中按顺序执行所有操作?很难用 .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);
}

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

可能的修复??

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

最佳答案

一般来说,如果您已经在使用基于任务的方法,通常最好尽量避免启动新任务。链接任务而不是显式阻塞将减少系统的开销,因为它不会让 ThreadPool 线程等待。

话虽这么说,直接阻止通常会更简单。

请注意,C# 5 使这变得更加简单,它提供了一个 API 来让您兼顾两者:

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

    var initialData = await SomeOtherMethodWhichReturnsTask(token);

    string result = await initialData.MethodWhichAlsoReturnsTask(token);

    return result;
};

更新后编辑:

鉴于新代码,没有一种简单的方法可以直接将其与 ContinueWith 链接起来.有几个选项。您可以使用 Unwrap转换 Task<Task<string>>你会创建,即:

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> 优雅地处理自己的展开。 :

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;
}

这允许整个进程在不创建新任务(绑定(bind)线程池线程)的情况下工作,并且永远不会阻塞。

请注意,您可能希望在取消时添加延续,并使用 tcs.SetCancelled当请求取消时,也是如此。

关于c# - 返回任务时链接任务的正确方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11783277/

相关文章:

c# - Azure函数从服务总线读取队列消息两次

.net - 异步等待方法比较

c# - 如何使异步函数(如 OpenStreamForWriteAsync)同步?

c# - system.threading.task - 为什么不发生 TaskScheduler.UnobservedTaskException 事件?我可以解决这个问题吗?

c# - 从基类继承 href 和 id 的 ASP.NET WebApi 响应模型中的排序属性

c# - Linq 最大 : The cast to value type 'System.Decimal' failed because the materialized value is null

c# - 将带有逗号的 double 值转换为带有点的变量以在 sql 语句中使用

c# - 在 C# 中使用 ref 参数在堆栈上会发生什么?

c# - Task.WaitAll 不等待子任务?

vb.net - 使用 Async-await 时 UI 卡住