Partilhar via


Gerenciando simultaneidade com DependentTransaction

O Transaction objeto é criado usando o DependentClone método. Seu único objetivo é garantir que a transação não possa ser confirmada enquanto algumas outras partes de código (por exemplo, um thread de trabalho) ainda estão executando trabalho na transação. Quando o trabalho realizado 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, é possível preservar a consistência e a correção dos dados.

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

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 for chamado na transação pai antes que o clone dependente indique que está pronto para a transação ser 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é que a transação pai atinja o tempo limite ou até Complete que todos os dependentes sejam chamados indicando sua conclusão. Isso é útil quando o cliente não deseja que a transação pai seja confirmada até que as transações dependentes sejam concluídas. Se o pai terminar seu trabalho antes da transação dependente e chamar Commit a transação, o processo de confirmação será bloqueado em um estado em que o trabalho adicional pode ser feito na transação e novos alistamentos podem ser criados, até que todos os dependentes chamem Complete. Assim que todos eles terminarem seu trabalho e ligarem Complete, o processo de confirmação para a transação começa.

  • RollbackIfNotComplete, por outro lado, cria uma transação dependente que aborta automaticamente se Commit for chamada na transação pai antes Complete de ser chamada. Neste caso, todo o trabalho feito na transação dependente está intacto dentro de um tempo de vida da transação, e ninguém tem a chance de comprometer apenas uma parte dele.

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çado. Depois que essa chamada for invocada, você não deve tentar nenhum 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 atual (ambiente) chamando o DependentClone método na transação atual e passar 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 parâmetro ThreadMethod .

Como a transação dependente é criada com BlockCommitUntilCompleteo , 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 seja chamado na transação dependente Complete . Isso significa que, se o escopo do cliente terminar (quando ele tentar descartar o objeto de transação no final da instrução) antes que using o novo thread chame Complete a transação dependente, o código do cliente será bloqueado até Complete que seja chamado no dependente. Em seguida, a transação pode terminar de confirmar ou abortar.

Problemas de simultaneidade

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

  • Se o thread de trabalho reverter a transação, mas o pai tentar confirmá-la, um TransactionAbortedException será lançado.

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

  • Se o thread de trabalho gerar 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