使用 DependentTransaction 管理并发

Transaction 对象是使用 DependentClone 该方法创建的。 其唯一目的是保证事务无法提交,而其他一些代码段(例如工作线程)仍在对事务执行工作。 当克隆事务中的工作完成且准备好提交时,可以使用Complete方法通知事务创建者。 因此,可以保留数据的一致性和正确性。

DependentTransaction 类还可用于管理异步任务之间的并发。 在这种情况下,父进程可以在依赖克隆独立处理其自身任务的同时继续执行任何代码。 换句话说,直到依赖的克隆完成时,父事务的执行才会被阻止。

创建从属克隆

若要创建依赖事务,请调用 DependentClone 方法,并将 DependentCloneOption 枚举作为参数传递。 如果在依赖克隆表明它已准备好提交事务(通过调用 Complete 方法)之前,对父事务调用 Commit,则此参数定义事务的行为。 以下值对此参数有效:

  • BlockCommitUntilComplete 创建一个依赖事务,该事务阻止父事务的提交过程,直到父事务超时,或者直到对所有依赖项调用 Complete 以指示它们已完成。 当客户端希望在依赖事务完成后再提交父事务时,这一点十分有用。 如果父事务的工作比该依赖事务早完成,并在此时对该依赖事务调用了 Commit,则提交进程会被阻止(在该状态下可在依赖事务上执行其他工作并可创建新登记),直到所有依赖事务都调用 Complete 为止。 只要所有依赖事务完成了工作并调用了 Complete,就会立即启动事务的提交进程。

  • 另一方面,在调用 RollbackIfNotComplete 之前对父事务调用 Commit 时,Complete 会创建一个自动中止的依赖事务。 在这种情况下,在该依赖事务中执行的所有工作在一个事务生存期内都保持不变,并且根本无法只提交其中的一部分。

Complete方法必须仅在应用程序完成对依赖事务的工作时调用一次;否则,将抛出InvalidOperationException。 调用此方法后,不得尝试对事务执行任何其他操作,否则将抛出异常。

下面的代码示例演示如何创建依赖事务来管理两个并发任务,方法是克隆依赖事务并将其传递给工作线程。

public class WorkerThread  
{  
    public void DoWork(DependentTransaction dependentTransaction)  
    {  
        Thread thread = new Thread(ThreadMethod);  
        thread.Start(dependentTransaction);
    }  
  
    public void ThreadMethod(object transaction)
    {
        DependentTransaction dependentTransaction = transaction as DependentTransaction;  
        Debug.Assert(dependentTransaction != null);
        try  
        {  
            using(TransactionScope ts = new TransactionScope(dependentTransaction))  
            {  
                /* Perform transactional work here */
                ts.Complete();  
            }  
        }  
        finally  
        {  
            dependentTransaction.Complete();
             dependentTransaction.Dispose();
        }  
    }  
  
//Client code
using(TransactionScope scope = new TransactionScope())  
{  
    Transaction currentTransaction = Transaction.Current;  
    DependentTransaction dependentTransaction;
    dependentTransaction = currentTransaction.DependentClone(DependentCloneOption.BlockCommitUntilComplete);  
    WorkerThread workerThread = new WorkerThread();  
    workerThread.DoWork(dependentTransaction);  
    /* Do some transactional work here, then: */  
    scope.Complete();  
}  

客户端代码创建一个事务范围,并设置环境事务。 不能直接将环境事务传递给辅助线程。 相反,应通过对当前事务调用 DependentClone 方法来克隆当前(环境)事务,并将从属事务传递给工作线程。

该方法 ThreadMethod 在新线程上执行。 客户端启动一个新线程,将依赖事务作为 ThreadMethod 参数传递。

由于依赖事务是用 BlockCommitUntilComplete 创建的,因此应保证第二个线程上的所有事务工作都完成并对该依赖事务调用 Complete 后才能提交事务。 这意味着如果客户端的范围在新线程对该依赖事务调用 Complete 之前结束(客户端尝试在 using 语句结束处释放事务对象时),则客户端代码会一直被阻止,直到对该依赖事务调用 Complete 为止。 这样,事务就可完成提交或中止。

并发问题

使用 DependentTransaction 类时需要注意的一些附加并发问题:

  • 如果辅助线程回滚事务而父事务却尝试提交它,则会引发 TransactionAbortedException

  • 应为事务中的每个工作线程创建新的依赖克隆。 不要将相同的依赖克隆传递给多个线程,因为只有其中一个线程可以调用 Complete 它。

  • 如果工作线程生成新的工作线程,请确保从已有的依赖克隆中创建一个新的依赖克隆,并将其传递给新线程。

另请参阅