ExecutionContext


Algum dia desses eu utilizei uma classe bastante interessante fornecida pelo namespace System.Threading, chamada ExecutionContext. Ela tem a capacidade de gerenciar o contexto de execução (segurança, sincronização e transações) da thread atual, fornecendo métodos que podemos utilizar para evitar ou possibilitar a propagação do contexto para os métodos assíncronos que a nossa aplicação possa utilizar.

Quando invocamos um método assíncrono, via delegate ou via ThreadPool, o contexto é sempre propagado entre as várias threads que a aplicação está utilizando e, conseqüentemente, voce terá acesso as mesmas informações com relação a execução da thread principal e, é neste cenário, que a classe em questão entra em ação. Ela fornece quatro métodos importantes, a saber:

  • Capture: Método estático que captura o contexto de execução atual.
  • SupressFlow: Suprime a propagação do contexto entre as threads assíncronas.
  • Run: Executa um método (através de um delegate ContextCallback) em um contexto específico.
  • RestoreFlow: Restaura a propagação do contexto entre as threads assíncronas.

Para exemplificar esses quatro métodos, o código abaixo utiliza uma função chamada LogonUser que permite efetuar o logon de um determinado usuário. Se a autenticação for realizada com sucesso, personificamos a thread atual para este usuário.

using System;
using System.Threading;
using System.Globalization;
using System.Security.Principal;
using System.Runtime.InteropServices;

namespace AsyncContext
{
    class Program
    {
        [DllImport(“advapi32.dll”)]
        private static extern bool LogonUser(
            String lpszUsername,
            String lpszDomain,
            String lpszPassword,
            int dwLogonType,
            int dwLogonProvider,
            out IntPtr phToken);

        static void Main(string[] args)
        {
            IntPtr token;
            if (LogonUser(“Teste”, string.Empty, “123456”, 2, 0, out token))
            {
                WindowsIdentity.Impersonate(token);
                ThreadPool.QueueUserWorkItem(Callback, 1);
                ExecutionContext ec = ExecutionContext.Capture();
                ExecutionContext.SuppressFlow();
                    ThreadPool.QueueUserWorkItem(Callback, 2);
                    ExecutionContext.Run(ec, new ContextCallback(Callback), 3);
                    ThreadPool.QueueUserWorkItem(Callback, 4);
                ExecutionContext.RestoreFlow();
                ThreadPool.QueueUserWorkItem(Callback, 5);
                token = IntPtr.Zero;
            }
            Console.ReadLine();
        }

        static void Callback(object state)
        {
            Console.WriteLine(“Id: {0}tThread: {1}tUser: {2}”,
                state,
                Thread.CurrentThread.ManagedThreadId,
                WindowsIdentity.GetCurrent().Name);
        }
    }
}

O método chamado Callback é utilizado pelos delegates para a execução do trabalho assíncrono. Ele exibe em seu interior informações como o estado (object) que é passado como parametro para o método, id da thread corrente, o nome do usuário corrente.

O resultado da execução deste código é:

Id: 1   Thread: 3   User: ISRAELAECETeste
Id: 2   Thread: 3   User: ISRAELAECEIsrael Aece
Id: 3   Thread: 1   User: ISRAELAECETeste
Id: 4   Thread: 3   User: ISRAELAECEIsrael Aece
Id: 5   Thread: 3   User: ISRAELAECETeste

Analisando o código do método Main, se o logon for realizado com sucesso, personificamos a thread atual para o usuário chamado Teste. Em seguida delegamos para a classe ThreadPool a execução do método Callback de forma assíncrona e resultará na exibição do usuário Teste (Id: 1). A partir daqui entra em cena a classe que é tema deste post.

Através do método estático Capture da classe ExecutionContext, ela retorna um objeto do mesmo tipo, representando o contexto atual e armazena em um objeto chamado ec. Agora, através do método SupressFlow, ele evita de propagar o contexto para os métodos assíncronos e, consequentemente, as requisições realizadas através do ThreadPool, resultarão em um outro nome de usuário (Id: 2). Como vimos acima, o método Run, dado um contexto e um delegate, ele é responsável por executar o método no contexto informado e, como podemos notar no código, passamos para o método ele a instancia do contexto que capturamos mais acima e que está armazenado no objeto ec e, além dele, passamos também uma instancia do delegate ContextCallback, apontando qual será o método que deve ser executado dentro daquele contexto específico. O resultado, como poderíamos esperar, é o usuário Teste (Id: 3). Finalmente, restauramos a propagação do contexto quando invocamos o método RestoreFlow e, se analisarmos a última execução a partir da ThreadPool, temos como resultadao o usuário Teste (Id: 5) novamente.

E, como a documentação diz, enquanto o contexto está suprimido, se invocar o método Capture, ele retornará nulo. Se ainda desejar verificar se o contexto está ou não suprimido, pode analisar isso através de um método chamado IsFlowSuppressed que retorna um valor booleano indicando essa informação.

Anúncios

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s