使用事务与任务并行库

时间:2012-10-23 00:16:47

标签: c# task-parallel-library transactionscope

我有N个进程来运行SQL Server 2008.如果任何进程失败,我需要回滚所有其他进程。

我正在考虑使用TPL创建父任务和N子任务。所有这些都包含在transactionScope(IsolationLevel.ReadCommitted)中,但在下面的示例中, child2 会抛出错误(customers2不是有效的表),而 child1 则没有回滚。

我在这里做错了吗?还有其他方法可以管理这种情况吗?

这是我的测试代码:

修改 的 我使用当前事务上的DependClone修改了下面的代码。我认为是有效的。

try
        {
            using (TransactionScope mainTransaction = TransactionUtils.CreateTransactionScope())
            {
                var parentTransactionClone1 = Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete);
                var parentTransactionClone2 = Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete);

                var parentTask = Task.Factory.StartNew(() =>
                {
                    var childTask1 = Task.Factory.StartNew(() =>
                    {
                        using (TransactionScope childScope1 = new TransactionScope(parentTransactionClone1))
                        {
                            SqlConnection cnn = new SqlConnection("Server=.\\sqlexpress;Database=northwind;Trusted_Connection=True;");
                            cnn.Open();
                            SqlCommand cmd = new SqlCommand("update customers set city ='valXXX' where customerID= 'ALFKI'", cnn);
                            cmd.ExecuteNonQuery();
                            cnn.Close();
                            childScope1.Complete();
                        }

                        parentTransactionClone1.Complete();

                    }, TaskCreationOptions.AttachedToParent);

                    var childTask2 = Task.Factory.StartNew(() =>
                    {
                        using (TransactionScope childScope2 = new TransactionScope(parentTransactionClone2))
                        {
                            SqlConnection cnn = new SqlConnection("Server=.\\sqlexpress;Database=northwind;Trusted_Connection=True;");
                            cnn.Open();
                            SqlCommand cmd = new SqlCommand("update customers2  set city ='valyyy' where customerID= 'ANATR'", cnn);
                            cmd.ExecuteNonQuery();
                            cnn.Close();

                            childScope2.Complete();
                        }

                        parentTransactionClone2.Complete();

                    }, TaskCreationOptions.AttachedToParent);
                });

                parentTask.Wait();
                mainTransaction.Complete();
            }
        }
        catch (Exception ex)
        {
            // manage ex               
        }
public static TransactionScope CreateTransactionScope()
    {
        var transactionOptions = new TransactionOptions();
        transactionOptions.IsolationLevel = IsolationLevel.ReadCommitted;
        transactionOptions.Timeout = TransactionManager.MaximumTimeout;            
        return new TransactionScope(TransactionScopeOption.Required, transactionOptions);
    }

2 个答案:

答案 0 :(得分:4)

TransactionScope类设置当前线程的环境事务(另请参阅Transaction.Current

您至少应该假设每个任务都在一个单独的线程中运行(尽管这不是TPL的必要条件)。

查看the relevant article的备注部分中的“重要”框 - 如果要在线程之间共享事务,则需要使用DependentTransaction类。

就个人而言,我确信在多个线程之间共享事务的整个工具在技术上是有效的,但是,我总是发现编写一个每个线程使用单独事务的设计更容易。

答案 1 :(得分:0)

任务并行库无法自己弄清楚任务的细节,也不会自动回滚,你可以做的最接近的是你定义的另一个任务child1-rollback的父任务,只有当child1失败了,您可以通过将TaskContinuationOption设置为OnlyOnFailure来非常好地定义,这样任务只有在child1失败时才会执行,同样可以说是关于child2。