Partager via


Utilisation des objets Windows Runtime dans un environnement multithread

Cet article explique la manière dont .NET Framework gère les appels du code C# et Visual Basic aux objets fournis par Windows Runtime ou les composants Windows Runtime.

Dans .NET Framework, vous pouvez par défaut accéder à un objet à partir de plusieurs threads, sans une gestion spéciale. Il vous suffit d'avoir une référence à l'objet. Dans Windows Runtime, ces objets sont appelés des objets agiles. La plupart des classes Windows Runtime sont agiles, mais certaines ne le sont pas, et même les classes agiles peuvent nécessiter une gestion spéciale.

Chaque fois que cela est possible, le Common Langage Runtime (CLR) traite les objets d'autres sources, par exemple Windows Runtime, comme s'il s'agissait d'objets .NET Framework :

  • Si l'objet implémente l'interface IAgileObject ou contient l'attribut MarshalingBehaviorAttribute avec MarshalingType.Agile, le CLR le traite comme un objet agile.

  • Si le CLR peut marshaler un appel du thread où il a été lancé au contexte de thread de l'objet cible, cette opération s'effectue de façon transparente.

  • Si l'objet contient l'attribut MarshalingBehaviorAttribute avec MarshalingType.None, la classe ne fournit pas les informations de marshaling. Le CLR ne peut pas marshaler l'appel, donc il lève une exception InvalidCastException avec un message indiquant que l'objet ne peut être utilisé que dans le contexte de thread où il a été créé.

Les sections suivantes décrivent les effets de ce comportement sur des objets de différentes sources.

Objets d'un composant Windows Runtime écrit en C# ou Visual Basic

Tous les types du composant qui peuvent être activés sont agiles par défaut.

Notes

L'agilité n'implique pas la sécurité des threads. Dans Windows Runtime et .NET Framework, la plupart des classes ne sont pas thread-safe car la sécurité des threads a un coût en termes de performances, et la plupart des objets ne sont jamais accessibles par plusieurs threads. Il est plus efficace de synchroniser l'accès aux objets (ou d'utiliser des classes thread-safe) uniquement en cas de nécessité.

Lorsque vous créez un composant Windows Runtime, vous pouvez remplacer la valeur par défaut. Consultez l'interface ICustomQueryInterface et l'interface IAgileObject.

Objets du Windows Runtime

La plupart des classes du Windows Runtime sont agiles, et le CLR les traite comme des classes agiles. La documentation de ces classes répertorie « MarshalingBehaviorAttribute(Agile) » parmi les attributs de classe. Toutefois, les membres de certaines de ces classes agiles, tels que les contrôles XAML, lèvent des exceptions s'ils ne sont pas appelés sur le thread d'interface utilisateur. Par exemple, le code suivant tente d'utiliser un thread d'arrière-plan pour définir une propriété du bouton sur lequel l'utilisateur a cliqué. La propriété Content du bouton lève une exception.

        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

Vous pouvez accéder en toute sécurité au bouton à l'aide de sa propriété Dispatcher ou de la propriété Dispatcher d'un objet qui existe dans le contexte du thread d'interface utilisateur (tel que la page sur laquelle se trouve le bouton). Le code suivant utilise la méthode RunAsync de l'objet CoreDispatcher pour distribuer l'appel sur le thread d'interface utilisateur.

        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

Notes

La propriété Dispatcher ne lève pas une exception lorsqu'elle a appelé à partir d'un autre thread.

La durée de vie d'un objet Windows Runtime créé sur le thread d'interface utilisateur est limitée par la durée de vie du thread. N'essayez pas d'accéder aux objets d'un thread d'interface utilisateur une fois la fenêtre fermée.

Si vous créez votre propre contrôle en héritant d'un contrôle XAML ou en composant un ensemble de contrôles XAML, votre contrôle est agile car il s'agit d'un objet .NET Framework. Toutefois, s'il appelle des membres de sa classe de base ou de ses classes constituantes, ou si vous appelez des membres hérités, ces membres lèvent des exceptions lorsqu'ils sont appelés à partir des threads à l'exception du thread d'interface utilisateur.

JJ157115.collapse_all(fr-fr,VS.120).gifClasses qui ne peuvent pas être marshalées

Les classes Windows Runtime qui ne fournissent pas d'informations de marshaling contiennent l'attribut MarshalingBehaviorAttribute avec MarshalingType.None. La documentation de cette classe répertorie « MarshalingBehaviorAttribute(None) » parmi ses attributs.

Le code suivant crée un objet CameraCaptureUI sur le thread d'interface utilisateur, puis tente de définir une propriété de l'objet à partir d'un thread du pool de threads. Le CLR ne peut pas marshaler l'appel et lève une exception InvalidCastException avec un message indiquant que l'objet ne peut être utilisé que dans le contexte de thread où il a été créé.

        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

La documentation de l'objet CameraCaptureUI répertorie également « ThreadingAttribute(STA) » parmi les attributs de la classe, car l'objet doit être créé dans un contexte monothread tel que le thread d'interface utilisateur.

Si vous souhaitez accéder à l'objet CameraCaptureUI à partir d'un autre thread, vous pouvez mettre en cache l'objet CoreDispatcher pour le thread d'interface utilisateur et l'utiliser ultérieurement pour distribuer l'appel sur ce thread. Vous pouvez également obtenir le répartiteur à partir d'un objet XAML tel que la page, comme indiqué dans le code suivant.

        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

Objets d'un composant Windows Runtime écrit en C++

Par défaut, les classes du composant qui peuvent être activées sont agiles. Toutefois, C++ offre un contrôle important sur les modèles de thread et le comportement de marshaling. Comme décrit précédemment dans cet article, le CLR reconnaît les classes agiles, tente de marshaler les appels lorsque les classes ne sont pas agiles et lève une exception InvalidCastException lorsqu'une classe ne contient pas d'informations de marshaling.

Pour les objets qui s'exécutent sur le thread d'interface utilisateur et qui lèvent des exceptions lorsqu'ils sont appelés à partir d'un thread autre que le thread d'interface utilisateur, vous pouvez utiliser l'objet CoreDispatcher du thread d'interface utilisateur pour distribuer l'appel.

Voir aussi

Concepts

Référence du langage Visual Basic et C#