我正在尝试整合async/ await进入我们的服务总线.我SingleThreadSynchronizationContext基于这个例子http://blogs.msdn.com/b/pfxteam/archive/2012/01/20/10259049.aspx实现了一个.
它的工作正常,除了一件事:TransactionScope.我等待内部的东西TransactionScope,它打破了TransactionScope.
TransactionScope似乎并没有与async/ 好玩await,当然因为它使用的东西存储在线程中ThreadStaticAttribute.我得到这个例外:
"TransactionScope嵌套不正确."
我TransactionScope在排队任务之前尝试保存数据并在运行之前将其恢复,但它似乎没有改变.和TransactionScope代码是一个烂摊子,所以真的很难理解发生了什么事情在那里.
有没有办法让它发挥作用?有替代品TransactionScope吗?
我使用基于任务的操作生成了代理.
如何使用async/await 正确调用此服务(处理ServiceClient和OperationContext之后)?
我的第一次尝试是:
public async Task<HomeInfo> GetHomeInfoAsync(DateTime timestamp)
{
using (var helper = new ServiceHelper<ServiceClient, ServiceContract>())
{
return await helper.Proxy.GetHomeInfoAsync(timestamp);
}
}
Run Code Online (Sandbox Code Playgroud)
作为ServiceHelper一个创造ServiceClient和OperationContextScope后来处理它们的类:
try
{
if (_operationContextScope != null)
{
_operationContextScope.Dispose();
}
if (_serviceClient != null)
{
if (_serviceClient.State != CommunicationState.Faulted)
{
_serviceClient.Close();
}
else
{
_serviceClient.Abort();
}
}
}
catch (CommunicationException)
{
_serviceClient.Abort();
}
catch (TimeoutException)
{
_serviceClient.Abort();
}
catch (Exception)
{
_serviceClient.Abort();
throw;
}
finally …Run Code Online (Sandbox Code Playgroud) 此问题已由EF数据上下文 - 异步/等待和多线程触发.我已经回答了那个,但没有提供任何最终的解决方案.
最初的问题是,有许多有用的.NET API(如Microsoft Entity Framework DbContext),它们提供了设计用于的异步方法await,但它们被记录为不是线程安全的.这使它们非常适合在桌面UI应用程序中使用,但不适用于服务器端应用程序.[编辑] 这可能实际上并不适用DbContext,这是微软关于EF6线程安全的声明,自己判断. [/ EDITED]
还有一些已建立的代码模式属于同一类别,例如调用WCF服务代理OperationContextScope(在此处和此处询问),例如:
using (var docClient = CreateDocumentServiceClient())
using (new OperationContextScope(docClient.InnerChannel))
{
return await docClient.GetDocumentAsync(docId);
}
Run Code Online (Sandbox Code Playgroud)
这可能会失败,因为OperationContextScope在其实现中使用线程本地存储.
问题的根源AspNetSynchronizationContext是在异步ASP.NET页面中使用,以便从ASP.NET线程池中使用更少的线程来满足更多HTTP请求.使用时AspNetSynchronizationContext,await可以在与启动异步操作的线程不同的线程上对延续进行排队,同时将原始线程释放到池中,并可用于提供另一个HTTP请求.这大大提高了服务器端代码的可扩展性.该机制在It's All About the SynchronizationContext中有详细描述,必须阅读.因此,虽然没有涉及并发API访问,但潜在的线程切换仍然阻止我们使用上述API.
我一直在考虑如何在不牺牲可扩展性的情况下解决这个问题.显然,恢复这些API的唯一方法是维护可能受线程切换影响的异步调用范围的线程关联.
假设我们有这样的线程亲和力.这些调用中的大多数都是IO绑定的(没有线程).当异步任务处于挂起状态时,它所源自的线程可用于提供另一个类似任务的延续,该结果已经可用.因此,它不应该过多地损害可伸缩性.这种方法并不新鲜,事实上,Node.js成功使用了类似的单线程模型 …
(SQL SERVER 2008)如果在TransactionScope(.Complete())中发生事务超时错误,您是否希望回滚事务?
更新:
错误实际上是在结束大括号(即.Dispose())中抛出,而不是.Complete().完整错误是:
The transaction has aborted. System.Transactions.TransactionAbortedException TransactionAbortedException System.Transactions.TransactionAbortedException: The transaction has aborted. ---> System.TimeoutException: Transaction Timeout
--- End of inner exception stack trace ---
at System.Transactions.TransactionStateAborted.BeginCommit(InternalTransaction tx, Boolean asyncCommit, AsyncCallback asyncCallback, Object asyncState)
at System.Transactions.CommittableTransaction.Commit()
at System.Transactions.TransactionScope.InternalDispose()
at System.Transactions.TransactionScope.Dispose()
Run Code Online (Sandbox Code Playgroud)
据我所知,事务没有回滚,表格保持锁定,直到我对SPID/session_id发出了KILL.
我使用DBCC OPENTRAN获取最早的事务,然后杀死它.我已经尝试了KILL WITH STATUS但是收到一条消息,说明没有任何状态可用,因为没有回滚.sys.dm_exec_sessions中SPID/session_id的状态为"正在休眠".代码段:
try
{
using (var transaction = new TransactionScope())
{
LOTS OF WORK CARRIED OUT WITH LINQ ENTITIES/SubmitChanges() etc.
transaction.Complete(); //Transaction timeout
}
return result;
}
catch (Exception ex)
{
logger.ErrorException(ex.Message, ex);
result.Fail(ex.Message); …Run Code Online (Sandbox Code Playgroud)