最近我已经开始与MySQL驱动工作了C# https://github.com/mysql/mysql-connector-netC#MySQL驱动程序 - 异步操作
与异步工作/等待我试图在并行任务
运行简单的SELECT查询,这基本上是如何代码如下:
private async Task<List<string>> RunQueryA()
{
List<string> lst = new List<string>();
using (MySqlConnection conn = new MySqlConnection(someConnectionString))
using (MySqlCommand cmd = conn.CreateCommand())
{
await conn.OpenAsync();
cmd.CommandText = "select someField from someTable ...";
using (var reader = await cmd.ExecuteReaderAsync())
{
// ...
}
}
return lst;
}
private async Task<List<string>> RunQueryB()
{
List<string> lst = new List<string>();
using (MySqlConnection conn = new MySqlConnection(someConnectionString))
using (MySqlCommand cmd = conn.CreateCommand())
{
await conn.OpenAsync();
cmd.CommandText = "select someField2 from someTable2 ...";
using (var reader = await cmd.ExecuteReaderAsync())
{
// ...
}
}
return lst;
}
public async Task Run()
{
await Task.WhenAll(RunQueryA(), RunQueryB());
}
我所期待的是为这两个查询并行运行,我所看到的是,RunQueryA()开始运行,只有一次有人做过RunQueryB可以开始了。
当然,它会暗示在查询中使用的一个或多个方法是阻塞的。 为了找到答案,我下载了最新的MySQL驱动程序源代码(从他们的github回购),并寻找异步方法的实现。
我看了例如在ExecuteReaderAsync的实现,它使我的基类System.Data.Common.DbCommand这是BCL
的一部分,我抬头一看那个类的.NET参考源 https://referencesource.microsoft.com/#System.Data/System/Data/Common/DBCommand.cs,1875e74763fd9ef2
我所看到的真糊涂我:
public Task<DbDataReader> ExecuteReaderAsync() {
return ExecuteReaderAsync(CommandBehavior.Default, CancellationToken.None);
}
public Task<DbDataReader> ExecuteReaderAsync(CancellationToken cancellationToken) {
return ExecuteReaderAsync(CommandBehavior.Default, cancellationToken);
}
public Task<DbDataReader> ExecuteReaderAsync(CommandBehavior behavior) {
return ExecuteReaderAsync(behavior, CancellationToken.None);
}
public Task<DbDataReader> ExecuteReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) {
return ExecuteDbDataReaderAsync(behavior, cancellationToken);
}
protected virtual Task<DbDataReader> ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) {
if (cancellationToken.IsCancellationRequested) {
return ADP.CreatedTaskWithCancellation<DbDataReader>();
}
else {
CancellationTokenRegistration registration = new CancellationTokenRegistration();
if (cancellationToken.CanBeCanceled) {
registration = cancellationToken.Register(CancelIgnoreFailure);
}
try {
return Task.FromResult<DbDataReader>(ExecuteReader(behavior));
}
catch (Exception e) {
registration.Dispose();
return ADP.CreatedTaskWithException<DbDataReader>(e);
}
}
}
这一切都归结到这条线:
return Task.FromResult<DbDataReader>(ExecuteReader(behavior));
在这一行中的ExecuteReader将同步运行,并阻止调用线程。
的ExecuteReader调用一个抽象方法
abstract protected DbDataReader ExecuteDbDataReader(CommandBehavior behavior);
这是MySQL驱动内覆盖:
MySQL的内部实现基本呼叫的ExecuteReader的同步版本...
简而言之,ExecuteReaderAsync()同步运行ExecuteReader()并阻塞调用线程。
请纠正我,如果我错了,但它确实似乎是这样的。
我不能找出究竟是谁在这里怪的的DbCommand类的BCL或MySQL驱动程序实现的...
一方面,MySQL驱动程序应该已经把它做为考虑, 另一方面,由于DbCommand提供了ExecuteDbDataReaderAsync的基本实现,它至少应该在工作线程中启动同步版本的ExecuteReader(更不用说使用实际的异步I/O了),因此它不会阻塞。
想什么呢?
作为解决方法,我该怎么做? 我可以直接启动ExecuteReaderAsync作为一项任务,但我不喜欢这个解决方案。
你有什么建议?
谢谢, 阿里克