【问题标题】:How to conditionally run a code asynchonously using tasks如何使用任务有条件地异步运行代码
【发布时间】:2011-12-20 13:04:25
【问题描述】:

我有一个负责检索资源的类,它还缓存它们以供快速访问。 该类公开了一个用于检索资源的异步方法:

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()));

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

谢谢。

【问题讨论】:

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


    【解决方案1】:

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

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

    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&lt;T&gt; 做同样的事情:

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

    【讨论】:

    • 这正是我想要的。谢谢!
    • +1 我在问这个问题 (stackoverflow.com/questions/15316613/…),现在我可以知道什么时候应该使用它了。
    • @joe 您能否为 Task.FromResult 增强此代码(附加解决方案)?
    • 调用Task.Factory.StartNew 默认使用TaskScheduler.Current,这在某些情况下可能会导致由StartNew 启动的任务在UI 线程上运行的意外行为。如果您希望您的代码始终在后台线程上运行,您应该始终传入TaskScheduler.Default 或使用Task.Run
    【解决方案2】:

    如果您有 UI 连接线程,请小心。

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

    为什么? Task.Run 默认使用 TaskScheduler.Default 参数包装 Task.Factory.StartNew,而不是 TaskScheduler.Current。

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

    你应该在 Task.Run 中调用它而不是它:

    • Task.Run(async() =&gt; await SomeTaskAsync());

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

    • Task.Run(() =&gt; SomeTask());

    【讨论】:

      猜你喜欢
      • 2023-04-06
      • 1970-01-01
      • 2022-01-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-17
      • 1970-01-01
      • 2020-11-09
      相关资源
      最近更新 更多