我在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();
}
答案 0 :(得分:22)
通常,如果您已经在使用基于任务的方法,通常最好避免启动新任务。链接任务而不是显式阻塞将减少系统的开销,因为它不会让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;
}
这允许整个过程在不创建新任务(绑定线程池线程)的情况下工作,而且不会阻塞。
请注意,您可能希望在取消时添加延续,并在请求取消时使用tcs.SetCancelled。
答案 1 :(得分:3)
这是我为解决此问题而构建的扩展方法。适用于.Net 4 +
public static Task<TNewResult> ContinueWith<T, TNewResult>(this Task<T> task, Func<Task<T>, Task<TNewResult>> continuationFunction, CancellationToken cancellationToken)
{
var tcs = new TaskCompletionSource<TNewResult>();
task.ContinueWith(t =>
{
if (cancellationToken.IsCancellationRequested)
{
tcs.SetCanceled();
}
continuationFunction(t).ContinueWith(t2 =>
{
if (cancellationToken.IsCancellationRequested || t2.IsCanceled)
{
tcs.TrySetCanceled();
}
else if (t2.IsFaulted)
{
tcs.TrySetException(t2.Exception);
}
else
{
tcs.TrySetResult(t2.Result);
}
});
});
return tcs.Task;
}
答案 2 :(得分:0)
是的,一切都将在您的主要任务中按顺序运行。这是因为调用Result属性将阻止当前线程,直到返回值为止。