Partilhar via


Usando objetos do Tempo de Execução do Windows em um ambiente multithreaded

Este artigo descreve como o .NET Framework trata chamadas do código C# e Visual Basic a objetos que são fornecidos pelo Tempo de Execução do Windows ou pelos componentes do Tempo de Execução do Windows.

No .NET Framework, por padrão, você pode acessar qualquer objeto de vários threads, sem tratamento especial. Tudo o que você precisa é de uma referência ao objeto. No Tempo de Execução do Windows, esses objetos são chamados de ágeis. A maioria das classes Tempo de Execução do Windows é ágil, mas algumas classes não são, e mesmo as classes ágeis podem exigir tratamento especial.

Sempre que possível, o CLR (Common Language Runtime) trata objetos de outras fontes, como o Tempo de Execução do Windows, como se fossem objetos do .NET Framework:

  • Se o objeto implementar a interface IAgileObject ou tiver o atributo MarshalingBehaviorAttribute com MarshalingType.Agile, o CLR o tratará como ágil.

  • Se o CLR puder realizar marshaling de uma chamada do thread onde ela foi criada para o contexto de threading do objeto de destino, isso será feito de forma transparente.

  • Se o objeto tiver o atributo MarshalingBehaviorAttribute com MarshalingType.None, a classe não fornecerá informações de marshaling. O CLR não pode realizar marshaling da chamada, assim, ele gera uma exceção InvalidCastException com uma mensagem informando que o objeto pode ser usado apenas no contexto de threading onde foi criado.

As seções a seguir descrevem os efeitos desse comportamento em objetos de várias fontes.

Objetos de um componente Tempo de Execução do Windows que é escrito em C# ou Visual Basic

Todos os tipos no componente que podem ser ativados são ágeis por padrão.

Dica

A agilidade não implica o acesso thread-safe. Tanto no Tempo de Execução do Windows quanto no .NET Framework, a maioria das classes não é thread-safe, pois o acesso thread-safe tem um custo de desempenho e a maioria dos objetos nunca é acessada por vários threads. É mais eficiente sincronizar somente o acesso aos objetos individuais (ou usar as classes thread-safe), conforme a necessidade.

Ao criar um componente Tempo de Execução do Windows, você pode substituir o padrão. Consulte a interface ICustomQueryInterface e a interface IAgileObject.

Objetos do Tempo de Execução do Windows

A maioria das classes no Tempo de Execução do Windows é ágil e o CLR as trata como ágeis. A documentação dessas classes lista "MarshalingBehaviorAttribute(Agile)" entre os atributos de classe. No entanto, os membros de algumas dessas classes, como controles XAML, geram exceções se não forem chamados no thread da interface de usuário. Por exemplo, o código a seguir tenta usar um thread em segundo plano para definir uma propriedade do botão que foi clicado. A propriedade Content do botão gera uma exceção.

        private async void Button_Click_2(object sender, RoutedEventArgs e)
        {
            Button b = (Button) sender;
            await Task.Run(() => { 
                b.Content += "."; 
            });
        }
    Private Async Sub Button_Click_2(sender As Object, e As RoutedEventArgs)
        Dim b As Button = CType(sender, Button)
        Await Task.Run(Sub()
                           b.Content &= "."
                       End Sub)
    End Sub

Você pode acessar o botão com segurança utilizando sua propriedade Dispatcher ou a propriedade Dispatcher de qualquer objeto existente no contexto do thread de interface do usuário (como a página em que o botão está ativado). O código a seguir usa o método RunAsync do objeto CoreDispatcher para distribuir a chamada no thread de interface do usuário.

        private async void Button_Click_2(object sender, RoutedEventArgs e)
        {
            Button b = (Button) sender;
            await b.Dispatcher.RunAsync(
                Windows.UI.Core.CoreDispatcherPriority.Normal, 
                () => { 
                    b.Content += "."; 
            });
        }
    Private Async Sub Button_Click_2(sender As Object, e As RoutedEventArgs)
        Dim b As Button = CType(sender, Button)
        Await b.Dispatcher.RunAsync(
            Windows.UI.Core.CoreDispatcherPriority.Normal, 
            Sub()
                b.Content &= "."
            End Sub)
    End Sub

Dica

A propriedade Dispatcher não gera uma exceção quando é chamada de outro thread.

O tempo de vida de um objeto Tempo de Execução do Windows que é criado no thread de interface do usuário é limitado pelo tempo de vida do thread. Não tente acessar objetos em um thread de interface do usuário depois que a janela tiver fechado.

Se você criar seu próprio controle herdando um controle XAML ou compondo um conjunto de controles XAML, seu controle será ágil porque é um objeto do .NET Framework. No entanto, se ele chamar membros da sua classe base ou de classes constituintes, ou se você chamar membros herdados, estes gerarão exceções quando forem chamados de qualquer thread, exceto do thread de interface do usuário.

JJ157115.collapse_all(pt-br,VS.120).gifClasses em que não é possível realizar marshaling

As classes Tempo de Execução do Windows que não fornecem informações sobre marshaling têm o atributo MarshalingBehaviorAttribute com MarshalingType.None. A documentação de tal classe lista "MarshalingBehaviorAttribute(None)" entre seus atributos.

O código a seguir cria um objeto CameraCaptureUI no thread de interface do usuário e tenta definir uma propriedade do objeto de um thread de pools de thread. O CLR não pode realizar marshaling da chamada e gera uma exceção InvalidCastException com uma mensagem informando que o objeto pode ser usado apenas no contexto de threading onde foi criado.

        Windows.Media.Capture.CameraCaptureUI ccui;

        private async void Button_Click_1(object sender, RoutedEventArgs e)
        {
            ccui = new Windows.Media.Capture.CameraCaptureUI();

            await Task.Run(() => { 
                ccui.PhotoSettings.AllowCropping = true; 
            });
        }
    Private ccui As Windows.Media.Capture.CameraCaptureUI

    Private Async Sub Button_Click_1(sender As Object, e As RoutedEventArgs)
        ccui = New Windows.Media.Capture.CameraCaptureUI()

        Await Task.Run(Sub()
                           ccui.PhotoSettings.AllowCropping = True
                       End Sub)
    End Sub

A documentação de CameraCaptureUI também lista "ThreadingAttribute(STA)" entre os atributos da classe, pois ele deve ser criado em um contexto de único thread, como o thread de interface do usuário.

Se desejar acessar o objeto CameraCaptureUI de outro thread, você poderá armazenar em cache o objeto CoreDispatcher para o thread de interface do usuário e usá-lo posteriormente para distribuir a chamada nesse thread. Ou pode obter o dispatcher de um objeto XAML, como a página, conforme mostrado no código a seguir.

        Windows.Media.Capture.CameraCaptureUI ccui;

        private async void Button_Click_3(object sender, RoutedEventArgs e)
        {
            ccui = new Windows.Media.Capture.CameraCaptureUI();

            await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
                () => {
                    ccui.PhotoSettings.AllowCropping = true;
                });
        }
    Dim ccui As Windows.Media.Capture.CameraCaptureUI

    Private Async Sub Button_Click_3(sender As Object, e As RoutedEventArgs)

        ccui = New Windows.Media.Capture.CameraCaptureUI()

        Await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
                                    Sub()
                                        ccui.PhotoSettings.AllowCropping = True
                                    End Sub)
    End Sub

Objetos de um componente Tempo de Execução do Windows que é escrito em C++

Por padrão, as classes no componente que podem ser ativadas são ágeis. No entanto, o C++ permite uma quantidade significativa de controles sobre modelos de threading e comportamento de marshaling. Conforme descrito anteriormente neste artigo, o CLR reconhece classes ágeis, tenta realizar marshaling de chamadas quando as classes não são ágeis e gera uma exceção InvalidCastException quando uma classe não tem informações de marshaling.

Para objetos que são executados no thread de interface do usuário e geram exceções quando são chamados de outro thread que não o thread de interface do usuário, você pode usar o objeto CoreDispatcher do thread de interface do usuário para distribuir a chamada.

Consulte também

Conceitos

Referência de linguagem de Visual Basic e C#