Compartilhar via


Gerenciar simultaneidade com DependentTransaction

O Transaction objeto é criado usando o DependentClone método. Sua única finalidade é garantir que a transação não possa ser confirmada enquanto algumas outras partes do código (por exemplo, um thread de trabalho) ainda estiverem executando trabalho na transação. Quando o trabalho feito dentro da transação clonada estiver concluído e pronto para ser confirmado, ele poderá notificar o criador da transação usando o Complete método. Assim, você pode preservar a consistência e a correção dos dados.

A DependentTransaction classe também pode ser usada para gerenciar a simultaneidade entre tarefas assíncronas. Nesse cenário, o pai pode continuar a executar qualquer código enquanto o clone dependente funciona em suas próprias tarefas. Em outras palavras, a execução do pai não é bloqueada até que o dependente é concluída.

Criando um Clone dependente

Para criar uma transação dependente, chame o DependentClone método e passe a DependentCloneOption enumeração como um parâmetro. Esse parâmetro define o comportamento da transação se Commit é chamado na transação pai antes que o clone dependente indica que ele está pronto para a transação seja confirmada (chamando o Complete método). Os seguintes valores são válidos para este parâmetro:

  • BlockCommitUntilComplete cria uma transação dependente que bloqueia o processo de confirmação da transação pai até a transação pai atingir o tempo limite ou até que Complete seja chamado em todos os dependentes indicando a conclusão. Isso é útil quando o cliente não deseja que a transação principal seja concluída até que as transações subordinadas tenham sido concluídas. Se o pai concluir seu trabalho antes que a transação dependente e chamadas Commit na transação, o processo de confirmação será bloqueado em um estado onde trabalho adicional pode ser feito na transação e novas inscrições podem ser criadas, até que todos os da chamada dependentes Complete. Assim que todos eles terminarem seu trabalho e fizerem a chamada Complete, o processo de confirmação da transação começa.

  • RollbackIfNotComplete, por outro lado, cria uma transação dependente que anula automaticamente se Commit for chamado na transação pai antes que Complete seja chamado. Nesse caso, todo o trabalho feito na transação dependente permanece intacto durante toda a duração da transação, e ninguém tem a oportunidade de comprometer apenas uma parte dela.

O Complete método deve ser chamado apenas uma vez quando seu aplicativo termina seu trabalho na transação dependente; Caso contrário, um InvalidOperationException é lançada. Depois que essa chamada é invocada, você não deve tentar qualquer trabalho adicional na transação ou uma exceção é lançada.

O exemplo de código a seguir mostra como criar uma transação dependente para gerenciar duas tarefas simultâneas clonando uma transação dependente e passando-a para um thread de trabalho.

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();  
}  

O código do cliente cria um escopo transacional que também define a transação ambiente. Você não deve passar a transação de ambiente para o thread de trabalho. Em vez disso, você deve clonar a transação (ambiente) atual chamando o DependentClone método na transação atual e passe o dependente para o thread de trabalho.

O ThreadMethod método é executado no novo thread. O cliente inicia um novo thread, passando a transação dependente como o ThreadMethod parâmetro.

Como a transação dependente é criada com BlockCommitUntilComplete, você tem a garantia de que a transação não pode ser confirmada até que todo o trabalho transacional feito no segundo thread seja concluído e Complete seja chamado na transação dependente. Isso significa que, se o escopo do cliente terminar (quando ele tentar descartar o objeto de transação no final da instrução using) antes do novo encadeamento chamar Complete na transação dependente, o código do cliente será bloqueado até que Complete seja chamado no dependente. Em seguida, a transação pode concluir a confirmação ou anulação.

Problemas de concorrência

Há alguns problemas de simultaneidade adicionais que você precisa estar ciente ao usar a DependentTransaction classe:

  • Se o thread de trabalho reverte a transação, mas o pai tenta confirmar, um TransactionAbortedException é lançada.

  • Você deve criar um novo clone dependente para cada thread de trabalho na transação. Não passam o mesmo clone dependente em vários threads, porque apenas um deles pode chamar Complete nele.

  • Se o thread de trabalho gera um novo thread de trabalho, certifique-se de criar um clone dependente do clone dependente e passá-lo para o novo thread.

Consulte também