单个事务中的多线程

本文关键字:多线程 事务 单个 | 更新日期: 2023-09-27 18:34:25

根据这篇 MSDN 文章,您应该能够在单个根事务中登记每个线程来多线程一个进程。

我基于该文章创建了一个示例,我希望事务被回滚(bool[] results应该全部falseforeach循环中(。不幸的是,情况并非如此,结果是可预测的不可预测的(运行该示例足够多次,您将看到数组中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();
        }
    }
}

单个事务中的多线程

您的结果[] 成员被设置为 true 不会神奇地将自己设置回 false(可悲的是(。这就是事务管理器所做的。查看 EnlistXXX 方法以了解所涉及的内容。

基本上,您需要在回滚时进行补偿。例如,您可以订阅根事务的 TransactionDone 事件,并检查事务是否已回滚。如果是,则需要还原已完成的童工的先前值。

您还可以处理要禁止的 TransactionAbortedException,或者在工作线程级别处理它(请参阅此页面上捕获它的示例:http://msdn.microsoft.com/en-us/library/ms973865.aspx(

通常,对于内存中的"事务",您最好使用任务库让工作人员批处理结果,然后在父任务的延续中"提交"它们。这比弄乱事务更容易,只有在内存和其他事务管理器(如 SQL Server 或其他进程(之间进行协调时才需要这样做。