如何使用任务有条不紊地运行代码

时间:2011-12-20 13:04:26

标签: c# .net multithreading .net-4.0 task-parallel-library

我有一个负责检索资源的课程,这些课程也会对它们进行快速访问。 该类公开了一种用于检索资源的异步方法:

public Task<object> GetResourceAsync(string resourceName)
{
    return Task.Factory.StartNew<object>(() =>
    {
        // look in cache

        // if not found, get from disk

        // return resource
    });
}

客户端代码如下所示:

myResourceProvider.GetResourceAsync("myResource")
    .ContinueWith<object>(t => Console.WriteLine("Got resource " + t.Result.ToString()));

这样,总是使用后台线程。但是,如果在缓存中找到对象,我不希望代码异步运行。 如果在缓存中找到它,我想立即返回资源而不必使用另一个线程。

感谢。

2 个答案:

答案 0 :(得分:22)

.NET 4.5具有Task.FromResult,允许您返回Task<T>,但它不是在线程池线程上运行委托,而是显式设置任务的返回值。

所以在你的代码的上下文中:

public Task<object> AsyncGetResource(string resourceName)
{
    object valueFromCache;
    if (_myCache.TryGetValue(resourceName, out valueFromCache)) {
        return Task.FromResult(valueFromCache);
    }
    return Task.Factory.StartNew<object>(() =>
    {
        // get from disk
        // add to cache
        // return resource
    });
}

如果您仍在使用.NET 4.0,则可以使用TaskCompletionSource<T>执行相同的操作:

var tcs = new TaskCompletionSource<object>();
tcs.SetResult(...item from cache...);
return tcs.Task;

答案 1 :(得分:0)

请注意是否有UI连接线程。

在WPF中,非常重要的一点是,要在UI线程上使用Task.Run(例如,按钮单击事件处理程序),以避免UI问题,并在后台线程上运行代码。

为什么?默认情况下,Task.Run使用TaskScheduler.Default参数而不是TaskScheduler.Current围绕Task.Factory.StartNew进行包装。

仅调用像这样的异步方法还不够,因为它正在UI线程上运行并冻结它:await SomeTaskAsync();

相反,您应该在Task.Run中调用它:

  • Task.Run(async() => await SomeTaskAsync());

或在Task.Run中使用您的syncron方法:

  • Task.Run(() => SomeTask());