Gerenciar simultaneidade com DependentTransaction
O Transaction objeto é criado usando o DependentClone método. Sua única finalidade é garantir que a transação não confirmada enquanto alguns outros trechos de código (por exemplo, um thread de trabalho) ainda estão executando o trabalho na transação. Quando o trabalho realizado dentro da transação clonada é concluído e pronto para ser confirmada, ele pode notificar o criador da transação usando o Complete método. Portanto, você pode preservar a consistência e correção dos dados.
O 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 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 passar o 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 esse 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 do pai seja confirmada até que as transações dependentes sejam 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 concluiu seu trabalho e chame Complete, inicia o processo de confirmação da transação.
RollbackIfNotComplete, por outro lado, cria uma transação dependente que anula automaticamente se Commit for chamado na transação pai antes Complete de ser chamado. Nesse caso, todo o trabalho feito na transação dependente está intacto no tempo de vida de uma transação e não tem a oportunidade de confirmar 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ç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 por uma transação dependente de clonagem e passá-la 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 de 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 segmento. O cliente inicia um novo thread, passando a transação dependente como o ThreadMethod
parâmetro.
Como a transação dependente é criada com BlockCommitUntilComplete, terá a garantia de que a transação não pode ser confirmada até que todos os do trabalho transacional feito no segundo thread é concluído e Complete é 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 simultaneidade
Existem alguns problemas de simultaneidade adicionais que precisam ser consideradas ao usar o 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.