什么时候"太多"异步并等待?所有方法都应该返回Task吗?

Gil*_*rdo 10 .net c# asp.net asynchronous async-await

我正在构建一个项目并使用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;
}
Run Code Online (Sandbox Code Playgroud)

Mat*_*zer 11

后面"的理念,所有异步 "是为了方便非阻塞I/O.

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

例如,ASP.NET Web API,ASP.NET MVC甚至ASP.NET Web窗体(代码隐藏)都可以利用所有异步来在执行某些异步操作时继续向其他用户提供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();
Run Code Online (Sandbox Code Playgroud)

那么......你需要异步实现一切吗?即使您返回a 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);
}
Run Code Online (Sandbox Code Playgroud)

我的观点是......

  • ......一切都实现返回任务,并使用Async后缀的方法的标识符的结束(WhateverAsync,WhoKnowsAsync,DoStuffAsync...)...

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

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

在一天结束时,您的方法具有异步签名,并且它们的依赖关系并不关心它们是否实际上是异步的,并且这些方法实现决定什么是异步或同步,而不是将此责任赋予调用者..


i3a*_*non 5

如果一个方法async里面没有任何操作,那么它就没有任何好处async.您应该只有具有操作的async方法async(I/O,DB等).

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

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