根据this MSDN article,您应该能够在单个根事务中使用每个线程多线程处理进程。
我根据那篇文章创建了一个示例,我期望回滚的回复(bool[] results
应该是false
循环中的全部foreach
。不幸的是,事实并非如此,并且结果是可预测的不可预测的(运行示例足够多次,您将看到数组中bool
值的任意组合)。
此外,我已经尝试了DependentCloneOption.BlockCommitUntilComplete
和DependentCloneOption.RollbackIfNotComplete
两者都没有产生预期结果。
其次,我认为ThreadPool.QueueUserWorkItem
充其量是丑陋的代码,而且使用Parallel.ForEach
代替这样的内容会很高兴。
最后,我的问题:)为什么这是不行的?我究竟做错了什么?是否只能在单个事务中包装多个线程?
namespace Playing
{
class Program
{
static bool[] results = new bool[] { false, false, false };
static void Main(string[] args)
{
try
{
using (var outer = new TransactionScope(
TransactionScopeOption.Required))
{
for (var i = 0; i < 3; i++ )
{
ThreadPool.QueueUserWorkItem(WorkerItem,
new Tuple<int, object>(
i, Transaction.Current.DependentClone(
DependentCloneOption.BlockCommitUntilComplete)));
}
outer.Complete();
}
}
catch { /* Suppress exceptions */ }
// Expect all to be false
foreach (var r in results)
Console.WriteLine(r);
}
private static void WorkerItem(object state)
{
var tup = (Tuple<int, object>)state;
var i = tup.Item1;
var dependent = (DependentTransaction)tup.Item2;
using (var inner = new TransactionScope(dependent))
{
// Intentionally throw exception to force roll-back
if (i == 2)
throw new Exception();
results[i] = true;
inner.Complete();
}
dependent.Complete();
}
}
}
答案 0 :(得分:3)
你的结果[]已被设置为true的成员不会神奇地将自己设置为假(遗憾)。这就是交易经理所做的。查看EnlistXXX方法,了解所涉及的内容。
基本上,您需要在回滚时进行补偿。例如,您可以订阅根事务的TransactionCompleted事件并检查事务是否已回滚。如果是,您将需要恢复已完成的子工作程序的先前值。
您还可以处理要抑制的TransactionAbortedException,或者在工作级别处理它(请参阅此页面上的捕获示例:http://msdn.microsoft.com/en-us/library/ms973865.aspx)
通常,对于内存中的“事务”,最好使用任务库让工作人员批量处理结果,然后在父任务的延续中“提交”它们。它比使用事务更容易,只有在内存和其他事务管理器(如SQL Server或其他进程)之间进行协调时才需要这样做。