C# 启用不带TransactionScopeAsyncFlowOption的异步TransactionScope。已启用
下面是使用事务作用域的异步缓存和数据库更新。我不能使用V4.5.1中引入的C# 启用不带TransactionScopeAsyncFlowOption的异步TransactionScope。已启用,c#,asynchronous,async-await,task-parallel-library,transactionscope,C#,Asynchronous,Async Await,Task Parallel Library,Transactionscope,下面是使用事务作用域的异步缓存和数据库更新。我不能使用V4.5.1中引入的TransactionScopeAsyncFlowOption.Enabled,因为我使用的Apache Ignite.Net缓存不支持它。我尝试通过捕获当前的同步上下文,然后显式地使用同步上下文发送方法来完成事务,以找到解决方法,但这不起作用,因为我仍然收到一个错误事务范围必须在创建它的同一线程上处理 关于如何实现异步更新的任何建议。Apache Ignite support的建议之一是使用以下内容: Task.When
TransactionScopeAsyncFlowOption.Enabled
,因为我使用的Apache Ignite.Net缓存不支持它。我尝试通过捕获当前的同步上下文
,然后显式地使用同步上下文发送
方法来完成事务,以找到解决方法,但这不起作用,因为我仍然收到一个错误事务范围必须在创建它的同一线程上处理
关于如何实现异步更新
的任何建议。Apache Ignite support的建议之一是使用以下内容:
Task.WhenAll(cacheUpdate,databaseUpdate).Wait()
,但这会使异步代码同步,因此不是最佳选项之一
public async Task Update()
{
// Capture Current Synchronization Context
var sc = SynchronizationContext.Current;
TransactionOptions tranOptions = new TransactionOptions();
tranOptions.IsolationLevel = System.Transactions.IsolationLevel.RepeatableRead;
using (var ts = new TransactionScope())
{
// Do Cache Update Operation as Async
Task cacheUpdate = // Update Cache Async
// Do Database Update Operation as Async
Task databaseUpdate = // Update Database Async
await Task.WhenAll(cacheUpdate, databaseUpdate);
sc.Send(new SendOrPostCallback(
o =>
{
ts.Complete();
}), sc);
}
}
在对博客和文章进行了大量搜索之后,我发现Stephen Toub的以下博客有助于在完全相同的线程上实现异步方法的延续,从而避免了事务范围问题。现在我不需要
TransactionScopeAsyncFlowOption.Enabled
在TransactionScope
void Main()
{
//修改了异步调度程序,使Continuations可以在完全相同的线程上工作
//在任务继续post WAIT需要相同线程的情况下需要
运行(async()=>等待DemoAsync());
“主完成”。转储();
}
静态异步任务demosync()
{
//交易范围测试(应处理)
使用(var ts=new TransactionScope())
{
等待缓存+数据库异步更新
ts.完成();
“事务范围完成”。Dump();
}
}
//Run方法来利用单线程同步上下文,从而确保
//控制特定线程集的线程/同步上下文post WAIT、持续运行
公共静态无效运行(Func Func)
{
//获取当前同步上下文
var prevCtx=SynchronizationContext.Current;
尝试
{
//创建SingleThreadSynchronizationContext
var syncCtx=新的SingleThreadSynchronizationContext();
//设置SingleThreadSynchronizationContext
SynchronizationContext.SetSynchronizationContext(syncCtx);
//Execute Func获取要执行的任务
var t=func();
//继续时,完成SingleThreadSynchronizationContext
t、 继续(
委托{syncCtx.Complete();},TaskScheduler.Default);
//确保SingleThreadSynchronizationContext在单个线程上运行
//在同一线程上执行任务及其继续
syncCtx.RunOnCurrentThread();
//获取结果(如果有)
t、 GetAwaiter().GetResult();
}
//重置上一个同步上下文
最后{SynchronizationContext.SetSynchronizationContext(prevCtx);}
}
//使用阻塞集合使用者/生产者模型重写同步上下文
//确保维护相同的同步上下文/线程/线程集
//在本例中,我们为continuation post wait主线程
私有密封类SingleThreadSynchronizationContext:SynchronizationContext
{
//封锁收集消费者-生产者模型
私有只读阻止集合
m_queue=new BlockingCollection();
//重写Post,在异步继续期间调用它
//发送用于同步继续
公共重写void Post(sendorpostd,对象状态)
{
m_queue.Add(
新的KeyValuePair(d,state));
}
//RunOnCurrentThread,如果从BlockingCollection获取对象,则执行该作业并执行它
public void RunOnCurrentThread()
{
键值对工作项;
while(m_queue.TryTake(out workItem,Timeout.Infinite))
工作项键(工作项值);
}
//竞争SynchronizationContext
public void Complete(){m_queue.CompleteAdding();}
}
我不太明白为什么不能使用TransactionScopeAsyncFlowOption.Enabled。您必须在.NET 4.0上运行?这将使用Apache Ignite.NET公开的缓存更新异步方法,这些方法与Java进程通信,并且根据它们,它们不支持此选项。我不知道为什么此功能需要ires从任何第三方组件获得了一些明确的支持。从他们的支持评论来看,如果我使用,也不例外,但根据他们的说法,他们还没有明确支持该功能。我想知道它必须在4.5.1之前的版本中实现,这对我来说已经足够好了,可以开始了。那么你的意思是如果你真的使用事务CopeAncySyncFlowOption.Enabled它运行良好,没有任何问题?
void Main()
{
// Modified Async Scheduler for Continuations to work on Exactly same thread
// Required in the case same Thread is required for Task Continuation post await
Run(async () => await DemoAsync());
"Main Complete".Dump();
}
static async Task DemoAsync()
{
// Transcation Scope test (shall dispose
using (var ts = new TransactionScope())
{
await Cache + Database Async update
ts.Complete();
"Transaction Scope Complete".Dump();
}
}
// Run Method to utilize the Single Thread Synchronization context, thus ensuring we can
// Control the threads / Synchronization context post await, cotinuation run of specific set of threads
public static void Run(Func<Task> func)
{
// Fetch Current Synchronization context
var prevCtx = SynchronizationContext.Current;
try
{
// Create SingleThreadSynchronizationContext
var syncCtx = new SingleThreadSynchronizationContext();
// Set SingleThreadSynchronizationContext
SynchronizationContext.SetSynchronizationContext(syncCtx);
// Execute Func<Task> to fetch the task to be executed
var t = func();
// On Continuation complete the SingleThreadSynchronizationContext
t.ContinueWith(
delegate { syncCtx.Complete(); }, TaskScheduler.Default);
// Ensure that SingleThreadSynchronizationContext run on a single thread
// Execute a Task and its continuation on same thread
syncCtx.RunOnCurrentThread();
// Fetch Result if any
t.GetAwaiter().GetResult();
}
// Reset the Previous Synchronization Context
finally { SynchronizationContext.SetSynchronizationContext(prevCtx); }
}
// Overriden Synchronization context, using Blocking Collection Consumer / Producer model
// Ensure that same Synchronization context / Thread / set of threads are maintained
// In this case we main a single thread for continuation post await
private sealed class SingleThreadSynchronizationContext : SynchronizationContext
{
// BlockingCollection Consumer Producer Model
private readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object>>
m_queue = new BlockingCollection<KeyValuePair<SendOrPostCallback, object>>();
// Override Post, which is called during Async continuation
// Send is for Synchronous continuation
public override void Post(SendOrPostCallback d, object state)
{
m_queue.Add(
new KeyValuePair<SendOrPostCallback, object>(d, state));
}
// RunOnCurrentThread, does the job if fetching object from BlockingCollection and execute it
public void RunOnCurrentThread()
{
KeyValuePair<SendOrPostCallback, object> workItem;
while (m_queue.TryTake(out workItem, Timeout.Infinite))
workItem.Key(workItem.Value);
}
// Compete the SynchronizationContext
public void Complete() { m_queue.CompleteAdding(); }
}