如何正确实现为异步使用而设计的接口?

Ste*_*han 1 c# async-await litedb

从以下(简化)接口开始,考虑到async/await,我想通过使用LiteDB数据库来实现它.

public interface IDataService
{
    Task<User> GetUserAsync(int key);
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,LiteDB目前不支持异步方法.所有方法都是同步的.

我尝试了以下内容,但后来遇到了一些问题(进程并没有真正等待任何原因).接下来,我读到你应该只Task.Run()用于CPU绑定算法,因此不能用于数据库访问.

public Task<User> GetUserAsync(int key)
{
    var task = Task.Run(() =>
    {
        return _users
            .Find(x => x.Key == key)
            .SingleOrDefault();
    });
    return task;
}
Run Code Online (Sandbox Code Playgroud)

即使我阅读了几篇博客文章和SO问题,我仍然不清楚如何为基于同步IO的代码制作一个正确编写的等待方法.

那么我该如何编写一个正确的等待方法呢?

注意:我无法更改界面.这是给出的.

Eri*_*ert 8

不能使用Task.Run.这不是道德上正确的事情.如您所知,将工作线程分配给不受CPU限制的任务在道德上是错误的,但这不是问题.这里的问题是LiteDB是单线程的,并且在尝试将调用移入工作线程时不会构建为健壮.

更新:显然LiteDB在版本4中是线程安全的,根据一位评论者可能比我在这个问题上更有信息.因此,请参阅LiteDB文档,了解它是什么类型的线程安全.并非所有线程安全对象都可以在没有任何限制的情况下使用.

你的选择是:

  • 放弃使用此界面.
  • 正如另一个答案所暗示的,撒谎.假设您的实现在实际上是同步的时是异步的.这可能导致您的用户界面挂起,但是,嘿,当您进行同步长时间运行的呼叫时,无论如何都会挂起您的用户界面.明确地调用FromResult是正确的方法.
  • 获得支持异步的更好的数据库.或者等待LiteDB成为更好的数据库,支持异步.
  • 所有调用移动到专用线程上LiteDB,包括创建和销毁与此数据库关联的每个对象.(可能在进程中,但是,如果你愿意的话,你可以把它放在自己的进程中.)为LiteDB实现你自己的异步前端,它适当地调用来自这个专用线程的调用.是的,您刻录了大部分时间都在睡觉的整个线程,但这是您使用同步数据库所付出的代价.

满足所有规定约束的解决方案是最后一个.它也是最适合你的那个.正如木工喜欢说的那样:你将节省在廉价材料上的费用.尝试将正确的异步接口改进到非异步但I/O绑定的单线程库是一个棘手的问题.祝好运!