不幸的是,MySQL 连接器不提供真正的异步方法。它的Begin/End 方法通过wrapping the synchronous version 在线程中伪造异步执行:
public IAsyncResult BeginExecuteReader(CommandBehavior behavior)
{
if (caller != null)
Throw(new MySqlException(Resources.UnableToStartSecondAsyncOp));
caller = new AsyncDelegate(AsyncExecuteWrapper);
asyncResult = caller.BeginInvoke(1, behavior, null, null);
return asyncResult;
}
AsyncExecuteWrapper 在哪里:
internal object AsyncExecuteWrapper(int type, CommandBehavior behavior)
{
thrownException = null;
try
{
if (type == 1)
return ExecuteReader(behavior);
return ExecuteNonQuery();
}
catch (Exception ex)
{
thrownException = ex;
}
return null;
}
因此,他们浪费了等待响应的线程。三年前提交了一个错误but never got a real answer
这就是为什么创建了这个替代的MySQLConnector 项目,它提供真正的异步操作以及.NET Core 支持,例如this method
internal async Task<int> ExecuteNonQueryAsync(IOBehavior ioBehavior, CancellationToken cancellationToken)
{
using (var reader = (MySqlDataReader) await ExecuteReaderAsync(CommandBehavior.Default, ioBehavior, cancellationToken).ConfigureAwait(false))
{
do
{
while (await reader.ReadAsync(ioBehavior, cancellationToken).ConfigureAwait(false))
{
}
} while (await reader.NextResultAsync(ioBehavior, cancellationToken).ConfigureAwait(false));
return reader.RecordsAffected;
}
}
你会看到ReadAsync也是一个合适的异步方法
差异很大,尤其是在 Web 应用程序中,因为它允许您使用 更小的 VM 实例来处理相同的流量。或者同一个虚拟机可以提供更多的流量。无论如何,价格差异是真实存在的。
这是因为异步网络操作实际上已卸载到驱动程序或主机。 IO 线程池中的线程仅在网络驱动程序向应用程序传递响应时使用。在半虚拟化的情况下,卸载可以一直到主机。
另一方面,伪造的同步操作会阻塞,甚至可能导致忙等待。这是因为同步原语旨在用于...同步访问共享资源。挂起线程成本,因此等待原语首先从自旋锁开始,并且仅在经过一定时间后才挂起线程。
这就是为什么不考虑异步的 Web 应用程序最终会在等待远程响应时消耗大量 CPU