首先是一个小小的上下文:它是一个Web应用程序,特别是这个通过nancy在mono上运行的自我托管,但Web应用程序作为上下文应该已经足够了。 ORM是ServiceStack的OrmLite,DB是postgres(但我认为问题+答案也适用于其他ORM和DB)。
想象一下这样的代码:
using (var transaction = Context.OpenTransaction())
{
Context.InsertAll<T1>(t1Data);
Context.InsertAll<T2>(t2Data);
Context.InsertAll<T3>(t3Data);
Context.InsertAll<T4>(t4Data);
transaction.Commit();
}
我正在打开一个事务,需要将不同的数据插入到不同的表中。当然我需要插入所有数据或不插入数据。现在一个人可能太聪明并且使用异步方法以便更快地完成它:
using (var transaction = Context.OpenTransaction())
{
var t1 = Context.InsertAllAsync<T1>(t1Data);
var t2 = Context.InsertAllAsync<T2>(t2Data);
var t3 = Context.InsertAllAsync<T3>(t3Data);
var t4 = Context.InsertAllAsync<T4>(t4Data);
await Task.WhenAll(t1, t2, t3, t4);
transaction.Commit();
}
据我所知,这不起作用。如果不同的表相互引用,它将无法确定,但即使它们不相同,数据库连接也不是线程安全的,并且为每个插入打开新连接将不一样,因为需要一个分布式事务(再次,引用不起作用)。所以,糟糕的主意。但是这个怎么样:
using (var transaction = Context.OpenTransaction())
{
await Context.InsertAllAsync<T1>(t1Data);
await Context.InsertAllAsync<T2>(t2Data);
await Context.InsertAllAsync<T3>(t3Data);
await Context.InsertAllAsync<T4>(t4Data);
transaction.Commit();
}
如果我正确理解nodeJS,而不是单线程,你基本上需要使用相当于第3个选项,否则你的整个应用程序将被卡住并且无法使用。
然而,.Net和Mono是多线程的。对我来说,...Async
函数似乎总是隐藏背景线程的蓬松方式。在这种情况下,1和3之间不会有任何真正的区别,除了1更快(没有额外的后台线程)并且更易于阅读。
但这是对的吗?或者它是否依赖于ORM,asyc函数是否真正异步并且不仅仅是启动后台线程。因为如果同一个线程可以同时处理其他任务(例如在nodejs中),那么第3个选项应该具有可伸缩性的优势。
我没有故意编写任何伪基准,因为我怀疑我的基准是否合适,而且.net(和mono)的体系结构的答案应该是显而易见的,如果立即等待异步调用是有意义的或不。