【问题标题】:When is "too much" async and await? Should all methods return Task? [closed]什么时候“太多”异步和等待?所有方法都应该返回任务吗? [关闭]
【发布时间】:2015-06-05 16:41:18
【问题描述】:

我正在构建一个项目并使用 async 和 await 方法。每个人都说异步应用程序是从头开始构建的,那么您真的应该有任何同步方法吗?您是否应该将所有方法都返回一个 Task 以便您可以异步使用?

让我们举一个简单的例子,我使用 Sql 将数据加载到集合中,这里有一些代码。

此代码使用ExecuteQueryAsync 方法从表中加载数据,GetQuery 方法构造SQL,但调用GetTableColumns。生成并执行 SQL 后,我会遍历集合并通过调用 GetDataFromReader 填充每个对象。

我的非异步方法应该是异步的吗?我是不是在想太多同步的编程方式而错过了什么?

public async Task<ICollection<MyObject>> ExecuteQueryAsync(Module module, List<SqlParameter> parameters)
{
    var result = new Collection<MyObject>();
    var query = GetQuery(module);

    using (var conn = new SqlConnection(_context.Database.Connection.ConnectionString))
    {
        await conn.OpenAsync();

        using (var cmd = new SqlCommand(query, conn))
        {
            if (parameters != null)
                cmd.Parameters.AddRange(parameters.ToArray());

            using (var dr = await cmd.ExecuteReaderAsync())
            {
                while (await dr.ReadAsync())
                {
                    result.Add(GetDataFromReader(module, dr));
                }

            }
        }

    }

    return result;
}

public string GetQuery(Module module)
{
    return "SELECT " + string.Join(",", GetTableColumns(module).ToArray()) + " FROM [TableA] ";
}

public List<string> GetTableColumns(Module module) 
{
    var columnNames = new List<string>();

    // get all list fields for the module
    var fields = (from a in module.Groups.SelectMany(a => a.Fields) select a).ToList();

    foreach (var field in fields)
    {
        if (field.Type == FieldType.List) {
            string query = "STUFF(";
            query += "(SELECT ';' + [Value] FROM [TableB] FOR XML PATH(''))";
            query += ", 1, 1, '') AS [" + field.ColumnName + "]";

            columnNames.Add(query);
        } else {
            columnNames.Add("[" + field.ColumnName + "]");
        }
    }

    return columnNames;
}

public MyObject GetDataFromReader(Module module, IDataReader dataReader)
{
    var entity = new MyObject();

    for (var i = 0; i < dataReader.FieldCount; i++)
    {
        object value = null;
        var fieldName = dataReader.GetName(i);

        if (!dataReader.IsDBNull(i))
        {
            value = dataReader.GetValue(i);
        }

        entity[fieldName] = value;
    }

    return entity;
}

【问题讨论】:

  • 这里没有固定的最终答案,这完全取决于。我的简短意见;如果一个方法本身不使用任何异步构造,那么它本身不应该是异步的,只是为了适应某些方法。
  • 只是一个小提示。我喜欢使用调用堆栈和相关工具进行调试,我会因此而讨厌你。 Async/Await 是相当复杂的实际代码的简单语法,在某些时候可能会咬你。
  • 您误解了“一路异步”的概念——它是关于让您的整个异步 调用层次结构 异步。也就是说,如果你调用一个异步方法,你自己应该是​​异步的,你的调用者和他的调用者等也应该是异步的。但是没有理由编写 string.JoinAsync 或类似的东西 - 事实上,它会适得其反。如果你只调用同步方法,你不应该是异步的——那只是误导。

标签: c# asp.net .net asynchronous async-await


【解决方案1】:

“all async”背后的理念是促进non-blocking I/O。

也就是说,您的主要异步代码可能会让环境优先考虑您的应用程序或服务的执行方式,并在多线程、多进程系统中实现尽可能多的并行执行。

例如,ASP.NET Web API、ASP.NET MVC 甚至 ASP.NET Web 窗体(代码隐藏)可以利用 all async 继续处理其他用户的 Web 请求,同时正在执行一些异步操作。因此,即使像 IIS 或 Katana 这样的 Web 服务器可能会限制并发请求的数量,异步操作也会在与请求线程不同的线程中执行,这允许 Web 服务器在异步操作得到结果的同时响应其他请求他们需要继续:

// While WhateverAsync is being executed, current thread can be used 
// by a new request and so on.
// Obviously, this will work this way if WhateverAsync actually
// does its work in another thread...
await WhateverAsync();

那么...您需要异步实现所有内容吗?即使您返回 Task,您也不需要提供异步实现:

public Task WhateverAsync()
{
    // This creates a fake Task object which 
    // simulates a Task that has already ended successfully
    // and without creating a child thread!
    // This, this method is a SYNCHRONOUS implementation unless
    // the whole method doesn't execute asynchronous operations.
    return Task.FromResult(true);
}

我的观点是……

  • ...实现所有返回任务并使用方法标识符末尾的Async 后缀(WhateverAsync、WhoKnowsAsync、DoStuffAsync...)...

  • ...除非您可以确定整个方法将始终执行一个非常简单的事情,不能长时间阻塞应用程序/服务的线程(长时间可以是几毫秒,现在想象一个代码每当调用某个方法时,它不会阻塞主应用程序线程 100 毫秒,并且您的代码可以在它等待 100 毫秒时优先执行某些操作....)。我会在这里包括字符串操作、简单的算术运算、配置方法...

如果您的代码今天不是异步的,您可以将其转换为实际的异步操作而不会影响整个代码库,因为您只需更改 Task.FromResult&lt;T&gt;(T result) 调用以实际返回未完成的 Task 实例。

归根结底,您的方法有一个异步签名,并且对它们的依赖并不关心它们是否实际上是异步的,这些方法实现决定什么是异步或同步,而不是将这个责任交给来电者。。

【讨论】:

    【解决方案2】:

    如果一个方法内部没有async 操作,则将其设为async 没有任何好处。您应该只在有 async 操作(I/O、DB 等)的情况下使用 async 方法。

    如果您的应用程序有很多这样的 I/O 方法并且它们遍布您的代码库,那并不是一件坏事。但不要只在同步方法上添加 async 关键字。

    在您的特定情况下,ExecuteQueryAsync 通过异步受益,因为它允许使用await cmd.ExecuteReaderAsync()。 GetTableColumns 和 GetDataFromReader 似乎是 CPU 密集型方法,它们不适合它们的 async-await 范例。

    【讨论】:

      猜你喜欢
      • 2019-01-28
      • 1970-01-01
      • 2019-05-07
      • 2014-10-01
      • 2018-09-03
      • 2013-06-04
      • 2018-04-21
      • 2017-03-26
      相关资源
      最近更新 更多