正在进行长时间运行的调用异步这么简单吗?

时间:2016-01-20 18:55:17

标签: c# asynchronous async-await c#-5.0

我只是,粗略的草案,使用以下模式将大量数据访问方法转换为异步,并且它看起来太简单,不足以满足以后的迭代。它有多安全,缺少什么,我该怎么做?

提供长时间通话的服务:

private class UserService
{
    public IdentityUser GetById(int id)
    {
        ...
    }
}

private UserService _userService = new UserService();

原始的同步方法:

public IdentityUser GetById(int id)
{
    return _userService.GetById(id);
}

我出色的新异步方法:

public async Task<IdentityUser> GetByIdAsync(int id)
{
    await Task.Run(() => _userService.GetById(id));
}

3 个答案:

答案 0 :(得分:5)

你不应该犯&#34;假异步&#34;像这样的方法:

public async Task<IdentityUser> GetByIdAsync(int id)
{
    await Task.Run(() => _userService.GetById(id));
}

我之所以称之为&#34;假异步&#34;是因为没有任何内在的异步操作。在这种情况下,您应该只有同步方法。如果调用者希望使用Task.Run使其异步,则可以执行此操作。

什么时候本质上是异步的?当您向Web服务或数据库发出请求时,例如,在发送请求和接收响应之间存在等待时间 - 请求是本质上异步操作。要避免阻塞调用线程,请使用async-await。

答案 1 :(得分:2)

从技术上讲,这是有效的,但它的工作原理是创建一个新线程来执行同步操作,该操作本身就是在一个固有的异步操作中包装和阻塞。这意味着您首先获得async的最大好处。

正确的方法是一路异步。现在你可能有这样的东西:

private class UserService
{
    public IdentityUser GetById(int id)
    {
        return mContext.Users.Single(u => u.Id == id);
    }
}

...您现在应该创建一个异步版本:

private class UserService
{
    public async Task<IdentityUser> GetByIdAsync(int id)
    {
        return await mContext.Users.SingleAsync(u => u.Id == id);
    }
}

用法:

public async Task<IdentityUser> GetByIdAsync(int id)
{
    return await _userService.GetByIdAsync(id);
}

当然,假设您的底层框架支持SingleAsync()等异步方法用于固有的异步操作,这将允许系统在您等待数据库操作完成时释放当前线程。该线程可以在其他地方重用,当操作完成后,您可以使用当时可用的任何线程。

它可能也值得一读并采用these Best Practices。您可能希望在任何您未访问会话和请求等上下文信息的地方使用.ConfigureAwait(false)

当然,这个答案假设GetById本质上是异步的:您从硬盘驱动器或网络位置或其他地方检索它。如果它使用长时间运行的CPU操作来计算用户的ID,那么Task.Run()是一个很好的方法,你可能还想另外指定它&#39} ; Task.Run()的参数中长期运行的任务。

答案 2 :(得分:0)

Task.Run()只应用于受CPU限制的工作。不记得为什么。 尝试改为使用GetByIdAsync()方法,最终调用异步资源。