繁体   English   中英

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

[英]Is making long-running calls async this simple?

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

提供长时间呼叫的服务:

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

你不应该像这样做“假异步”方法:

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

我称之为“假异步”的原因是因为没有任何关于操作的内在异步。 在这种情况下,您应该只有同步方法。 如果调用者想通过使用Task.Run使其异步,他可以这样做。

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

从技术上讲,这是有效的,但它的工作原理是创建一个新线程来执行同步操作,该操作本身就是在一个固有的异步操作中进行包装和阻塞。 这意味着你没有从一开始就获得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()用于固有的异步操作),这将允许系统在您等待数据库操作完成时释放当前线程。 该线程可以在其他地方重用,当操作完成后,您可以使用当时可用的任何线程。

它可能也值得阅读并采用这些最佳实践 例如,您可能希望在不访问会话和请求等上下文信息的任何地方使用.ConfigureAwait(false)

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM