【问题标题】:Executing multiple database calls - single or multi threaded?执行多个数据库调用 - 单线程还是多线程?
【发布时间】:2014-10-12 04:49:58
【问题描述】:

我试图了解在执行多个数据库调用时最好采用哪种方法。我有很多包含相同模式的屏幕:

  • 在屏幕加载时异步获取数据(async/await)
  • 将控制权返回给 UI 线程,以便可以加载屏幕
  • 在数据到达时更新 UI

有些界面只有一个数据库调用,有些界面有多个数据库调用。

一个典型的数据库调用

private Task<List<Product>> GetProductsAsync()
    {
        TaskCompletionSource<List<Product>> tcs = new TaskCompletionSource<List<Product>>();
        TaskEx.Run(() => tcs.SetResult(Repository.GetAllProducts()));
        return tcs.Task;
    }

单次通话在屏幕上的使用

// an event handler,can be marked as async void
public override async void OnActivated()
{
    base.OnActivated();
    ProductList = await GetProductsAsync();
}

在屏幕上多次调用

// an event handler,can be marked as async void
public override async void OnActivated()
{
    base.OnActivated();
    ProductList = await GetProductsAsync();
    CustomerList = await GetCustomersAsync();
    OtherList = await GetOthersAsync();
}

如您所见,当前实现将从屏幕上的线程池中生成多个线程,并进行多次调用。这是正确的做法吗?

另一种方法是在一个任务中检索所有数据,但这会导致在 tcs.Result 中检索列表数组,这有点不清楚正在检索什么(从代码可读性的角度来看)。

您认为在自己的线程中执行每个数据库调用是正确的方法吗?如果不是,您的建议是什么?

【问题讨论】:

  • 您当前的实现不会产生多个线程。在调用下一个方法之前,它会在您等待时一一执行。
  • @Sriram Sakthivel 第一次调用 products 将产生一个新线程,因为 TaskEx.Run 将从线程池中获取一个新线程。第二次呼叫、第三次呼叫等也是如此……
  • 对产品的第一次调用将产生一个新线程,但await 将异步等待GetProductsAsync 完成。只有这样它才会调用GetCustomersAsync。所以这里不需要多线程。完成GetProductsAsync 任务的同一线程可以执行GetCustomersAsync 任务等等。你明白我的意思了吗?如果您需要同时完成这三个操作,您需要同时启动它们并与await Task.WhenAll 一起等待它们
  • 您确定您的数据库可以处理同时查询吗?没有它,多线程将毫无意义。
  • @Goran 这就是我想说的。我没有说它将在单线程中执行。再次阅读我的评论。我说同一个线程可以执行下一个任务。我的意思是你是一件一件地做,而不是一口气做完。

标签: c# wpf mvvm async-await


【解决方案1】:

当您使用async-await 时,所有调用都可能在不同的线程上运行,但不一定同时(并发)。在您的情况下,所有调用都是顺序的,因此您不会同时访问数据库。

关于对数据库的并发调用,这取决于数据库本身。大多数数据库可以同时处理多个调用,尤其是在不同的表上。但是,如果并发操作使用相同的锁,它可能会增加争用。它可以是任何一种方式,但在大多数情况下,并发调用数据库是一件好事。


关于GetProductsAsync你为什么使用TaskCompletionSource而不是:

private Task<List<Product>> GetProductsAsync()
{
    return Task.Run(() => Repository.GetAllProducts());
}

【讨论】:

  • 我已经不使用并发呼叫了,所以我不明白你的建议?我正在使用不支持并发调用的 EF DbContexr,但我没有指定我使用的 ORM,因为在这种情况下它并不重要。我尽量避免有人建议使用新的 EF6 异步功能,因为我使用的是 4.0 框架,所以我无法使用该功能,并且 EF6 异步功能仅在 4.5+ 上受支持。
  • 关于你添加的部分,是的,这里 tsc 完全够用了。我的问题是,是否可以生成 5 个不同的线程来获取数据,或者我应该将所有调用包装到一个线程中。或者整体性能不受影响,我还是直接用第一个吧,比较好理解。
  • @Goran 你问哪个是正确的。单线程或多线程数据库调用。两者都是正确的,但并发调用可能会取得更好的结果。这就是我的答案。
  • @Goran 您的代码不是多线程的,也没有产生 5 个不同的线程。没有理由使用 TCS 创建任务,而不仅仅是使用您已有的任务。
  • 我的代码是多线程的,每次执行 TaskEx.Run() 时我都会创建新线程。在每个 Run() 上,我得到一个具有不同 ManagedThreadId 的新线程,这意味着每次发出不同的数据库调用时它都会分配不同的线程。我已经评论过 TCS...
猜你喜欢
  • 1970-01-01
  • 2021-02-20
  • 1970-01-01
  • 2018-08-16
  • 2011-09-29
  • 2011-11-22
  • 2021-12-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多