Usando objetos do Windows Runtime em um ambiente de vários threads

Este artigo discute a maneira como o .NET Framework lida com chamadas de código C# e Visual Basic para objetos fornecidos pelo Windows Runtime ou por componentes Windows Runtime.

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

Sempre que possível, o common language runtime (CLR) lida com objetos de outras fontes, como o Windows Runtime, como se fossem objetos do .NET Framework:

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

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

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

Observação

Agilidade não implica a segurança do thread. Tanto no Windows Runtime como no .NET Framework, a maioria das classes não são seguras porque a segurança do segmento tem um custo de desempenho e a maioria dos objetos nunca é acessada por múltiplos segmentos. É mais eficiente sincronizar o acesso a objetos individuais (ou usar classes seguras) apenas conforme necessário.

Quando você autoriza um componente do Tempo de Execução do Windows, você pode substituir o padrão. Consulte as interfaces ICustomQueryInterface e IAgileObject.

Objeto do Windows Runtime

A maioria das classes no Windows Runtime são ágeis, e o CLR as trata como ágil. A documentação para essas classes lista "MarshalingBehaviorAttribute(Agile)" entre os atributos da classe. No entanto, os membros de algumas dessas classes ágeis, como os controles XAML, lançam exceções se não forem chamados no segmento da interface de usuário. Por exemplo, o código a seguir tenta usar uma linha de fundo para definir uma propriedade do botão que foi clicado. A propriedade Conteúdo do botão lança 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 usando sua propriedade Expedidor, ou a propriedade Dispatcher de qualquer objeto que exista no contexto do segmento da interface de usuário (como a página em que o botão está). O código a seguir usa, do objeto CoreDispatcher, o método RunAsync para enviar a chamada no segmento da interface de 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

Observação

A propriedade Dispatcher não lança uma exceção quando ela é chamada a partir de outro segmento.

O tempo de vida de um objeto do Windows Runtime que é criado no segmento da interface de usuário é delimitado pelo tempo de vida do segmento. Não tente acessar objetos em um segmento da interface de usuário após a janela ter fechado.

Se você criar seu próprio controle ao herdar um controle XAML ou ao compor um conjunto de controles XAML, seu controle é ágil porque é um objeto do .NET Framework. No entanto, se ele chama membros da sua classe base ou de classes constituintes, ou se você chama membros herdados, esses membros lançarão exceções quando forem chamados de qualquer segmento, exceto o segmento da interface de usuário.

Classes que não podem ser integradas

Classes do Windows Runtime que não fornecem informações de integração possuem o atributo MarshalingBehaviorAttribute com MarshalingType.None. A documentação para tal classe lista "MarshalingBehaviorAttribute(None)" entre seus atributos.

O código a seguir cria um objeto CameraCaptureUI no segmento da interface de usuário, e então tenta definir uma propriedade do objeto a partir de um segmento do grupo de threads. O CLR não consegue fazer a chamada, e cria uma exceção InvalidCastException com uma mensagem indicando 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 do CameraCaptureUI também lista "ThreadingAttribute(STA)" entre os atributos da classe, porque ela deve ser criada em um contexto de segmento único, como o segmento da interface de usuário.

Se você quiser acessar o objeto CameraCaptureUI a partir de outro segmento você pode armazenar o objeto CoreDispatcher em cache para o segmento da interface de usuário, e usá-lo mais tarde para despachar a chamada nesse tópico. Ou você pode obter o expedidor 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 do Tempo de Execução do Windows que está 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 controle sobre os modelos de threading e sobre o comportamento de integração. Conforme descrito anteriormente neste artigo, o CLR reconhece classes ágeis, tenta ordenar chamadas quando as classes não são ágeis, e gera uma exceção System. InvalidCastException quando uma classe não tem nenhuma informação de integração.

Para objetos que são executados no segmento da interface de usuário e lançam exceções quando são chamados de um segmento diferente do segmento da interface de usuário, você pode usar o objeto CoreDispatcher do segmento da interface de usuário para despachar a chamada.

Consulte Também

Guia do C#

Guia do Visual Basic