使用Task.FromResult在同步函数周围创建异步包装器?

时间:2014-12-01 15:35:20

标签: c# async-await

我正在浏览asp.net vnext系列中的tutorial。我在tutorial中发现了一些没有多大意义的东西:

using System.Linq;
using Microsoft.AspNet.Mvc;
using TodoList.Models;
using System.Threading.Tasks;

namespace TodoList.ViewComponents
{
    public class PriorityListViewComponent : ViewComponent
    {
        private readonly ApplicationDbContext db;

        public PriorityListViewComponent(ApplicationDbContext context)
        {
            db = context;
        }

        // Synchronous Invoke removed.

        public async Task<IViewComponentResult> InvokeAsync(int maxPriority, bool isDone)
        {
            string MyView = "Default";

            // If asking for all completed tasks, render with the "PVC" view.
            if (maxPriority > 3 && isDone == true)
            {
                MyView = "PVC";
            }

            var items = await GetItemsAsync(maxPriority, isDone);

            return View(MyView, items);
        }

        private Task<IQueryable<TodoItem>> GetItemsAsync(int maxPriority, bool isDone)
        {
            return Task.FromResult(GetItems(maxPriority, isDone));

        }
        private IQueryable<TodoItem> GetItems(int maxPriority, bool isDone)
        {
            var items = db.TodoItems.Where(x => x.IsDone == isDone &&
                                                x.Priority <= maxPriority);

            string msg = "Priority <= " + maxPriority.ToString() +
                         " && isDone == " + isDone.ToString();
            ViewBag.PriorityMessage = msg;

            return items;
        }

    }
}

他们正在为同步方法创建一个包装器,只是调用Task.FromResults()所以它的异步。首先,它仍然会阻止这一点吗?可能在幕后制作TaskCompletionSource&amp;将结果包装在Task对象中。然后他们await结果可能只是创造了我认为不需要的额外开销。他们通过EF进行的数据库调用仍然是同步的。我在这里遗漏了什么或者是一个不好的例子吗?

3 个答案:

答案 0 :(得分:4)

这是一个糟糕的例子。在同步操作周围创建异步包装器没有任何价值,这样的决定通常应留给库使用者。

  

我认为应该公开的唯一异步方法是那些比同步方案具有可扩展性优势的方法。不应仅仅为了卸载而暴露异步方法:同步方法的消费者可以很容易地实现这些好处

来自Should I expose asynchronous wrappers for synchronous methods?

虽然有时候它是合适的(比如遵守Task返回接口或抽象方法),但似乎并非如此。

答案 1 :(得分:3)

你是完全正确的,它最终会阻塞,并且使一切都有一个异步API,但同步行为只会[略微]恶化。

至于你为什么要这样做;你这样做是为了向实际异步版本公开一个相同的API,允许两个实现都满足一个接口或其他类型的契约,允许它们被没有&#的调用者使用39;知道实际使用的是哪个实现。当应用程序转换为完全异步,但某些部分尚未具有异步实现时,它可能在此处用作停止间隙。当您升级数据库提供程序并且实际这些方法的异步版本时,它意味着您只需要更改此实现,并且不需要更改调用方的任何内容&#39事情的一面。

当然,如果最终让调用者期望这样的方法实际上是异步行为并且不会长时间阻塞,那么它有很大的潜在问题,因为它没有做到这一点一点都不。

这是一个黑客;不是没有目的,但你需要认识它。

答案 2 :(得分:0)

我必须同意I3arnon:它没有任何意义,只是混淆了这段代码的消费者。

该示例并未实现任何通用界面,因此在这种情况下,Servy关于公开相同API的论点并不合适。

方法存根:

private Task<IQueryable<TodoItem>> GetItemsAsync(int maxPriority, bool isDone) { .. }

明确声明它不仅返回一个任务(这不一定意味着异步性),而是通过它的名字,它 异步。

至于增加的开销,这里执行的唯一不必要的操作是创建Task对象。没有执行async / TPL代码。为此,我说这个例子只是误导。

如果这是我的代码,我会假设我一直忙着,期待看到

// TODO: Make this async!

在该方法中发表评论;)