Administrar la simultaneidad con DependentTransaction

Se crea un objeto Transaction utilizando el método DependentClone. Su solo propósito es garantizar que la transacción no pueda confirmar mientras algunas otras partes de código (por ejemplo, un subproceso de trabajo) todavía están realizando el trabajo en la transacción. Cuando finalmente se completa el proceso de la transacción clonada y está preparada para confirmarse, se puede notificar al creador de la transacción con el método Complete. Así se puede conservar la coherencia y exactitud de los datos.

La clase DependentTransaction también se puede utilizar para administrar la simultaneidad entre las tareas asincrónicas. En este escenario, el elemento primario puede continuar ejecutando cualquier código mientras el clon dependiente funciona en sus propias tareas. En otras palabras, no se bloquea la ejecución del elemento primario hasta que el dependiente complete.

Crear un clon dependiente

Para crear una transacción dependiente, llame al método DependentClone y pase la enumeración DependentCloneOption como un parámetro. Este parámetro define el comportamiento de la transacción si se llama Commit en la transacción primaria antes de que el clon dependiente indique que está listo para que la transacción se confirme (llamando al método Complete ). Los valores siguientes son válidos para este parámetro:

  • BlockCommitUntilComplete crea una transacción dependiente que bloquea el proceso de confirmación de la transacción primaria hasta que se agota el tiempo de espera de la transacción primaria, o hasta que se llame a Complete en todos los dependientes que indiquen su finalización. Esto es útil cuando el cliente no desea que la transacción primaria se confirme hasta que las transacciones dependientes se hayan completado. Si la primaria finaliza su trabajo antes que la transacción dependiente y llama Commit en la transacción, el proceso de la confirmación se bloquea en un estado donde el trabajo adicional se puede hacer en la transacción y se pueden crear nuevas inscripciones, hasta que todos los dependientes llamen Complete. En cuanto todos ellos hayan finalizado su trabajo y llamen a Complete, el proceso de la confirmación para la transacción comienza.

  • RollbackIfNotComplete, por otro lado, crea una transacción dependiente que automáticamente se anula en caso de que se llame a Commit en la transacción primaria antes de que Complete se haya llamado. En este caso, todo el trabajo hecho en la transacción dependiente está intacto dentro de una duración de la transacción y nadie tiene una oportunidad para simplemente confirmar una parte de él.

Se debe llamar al método Complete solo una vez cuando su aplicación finaliza su trabajo en la transacción dependiente; de lo contrario, se inicia InvalidOperationException. Después de que se invoca esta llamada, no debería intentar realizar ningún trabajo adicional en la transacción o se producirá una excepción.

El ejemplo de código siguiente muestra cómo crear una transacción dependiente para administrar dos tareas simultáneas clonando una transacción dependiente y pasándola a un subproceso de trabajo.

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

El código de cliente crea un ámbito transaccional que también establece la transacción ambiente. No debería pasar la transacción ambiente al subproceso de trabajo. En su lugar, debería clonar la transacción actual (ambiente) llamando al método DependentClone en la transacción actual y pasar el dependiente al subproceso de trabajo.

El método ThreadMethod se ejecuta en el nuevo subproceso. El cliente inicia un nuevo subproceso, pasando la transacción dependiente como el parámetro ThreadMethod.

Dado que la transacción dependiente se crea con BlockCommitUntilComplete, se garantiza que no se puede confirmar la transacción hasta que todo el trabajo transaccional hecho en el segundo subproceso esté finalizado y se llame a Complete en la transacción dependiente. Esto significa que si el ámbito del cliente finaliza (cuando intente eliminar el objeto de transacción al final de la instrucción using) antes de que el nuevo subproceso llame a Complete en la transacción dependiente, el código de cliente se bloquea hasta que se llame Complete en el dependiente. A continuación, la transacción puede terminar de confirmarse o anularse.

Problemas de simultaneidad

Existen problemas de simultaneidad adicionales de los que necesita ser consciente al utilizar la clase DependentTransaction:

  • Si el subproceso de trabajo deshace la transacción pero el elemento primario intenta confirmarlo, se inicia TransactionAbortedException.

  • Debería crear un nuevo clon dependiente para cada subproceso de trabajo en la transacción. No pase el mismo clon dependiente a varios subprocesos, porque solo uno de ellos puede llamar Complete en él.

  • Si el subproceso de trabajo genera un nuevo subproceso de trabajo, asegúrese de crear un clon dependiente a partir del clon dependiente y pasarlo al nuevo subproceso.

Consulte también