单事务中的多线程处理

4
根据这篇MSDN文章,你应该能够使用单个根事务在多线程进程中运行每个线程。
我基于那篇文章创建了一个示例程序,在其中期望回滚事务(foreach循环中的bool[] results应全部为false)。不幸的是,实际情况并非如此,结果是不可预测的(多次运行示例程序会看到数组中的任何bool值的组合)。
此外,我尝试了DependentCloneOption.BlockCommitUntilCompleteDependentCloneOption.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();
        }
    }
}

1
你正在寻找事务性内存,这在CLR上不受本地支持。 - usr
1个回答

4
你的results[]成员如果设置为true,它们不会自动恢复为false(遗憾的是)。这就是事务处理器的作用。查看EnlistXXX方法以了解涉及的内容。
基本上,在回滚事件中,您需要进行补偿。例如,您可以订阅根事务的TransactionCompleted事件,并检查是否已回滚该事务。如果是,则需要恢复已完成的子工作项的先前值。
您还可以处理您所压制的TransactionAbortedException异常,或在工作线程级别上处理它(请参见此页面上捕获它的示例:http://msdn.microsoft.com/en-us/library/ms973865.aspx
通常,在内存中使用“事务”时,最好使用Task库使工作项批量处理结果,然后在父任务的连续过程中“提交”它们。这比使用Transactions更容易,只有在在内存和某些其他事务管理器之间进行协调时才需要使用Transactions(如SQL Server或其他进程)。

很棒的答案。这只是我为了说明我想要的行为而制作的一个快速示例。我在实际应用中将使用Sql Server,但听起来在那种情况下也需要订阅事务并进行一些手动操作。谢谢。 - Didaxis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接