Windows Runtime-objecten gebruiken in een meerdraads omgeving

In dit artikel wordt beschreven hoe .NET Framework aanroepen van C# en Visual Basic-code verwerkt naar objecten die worden geleverd door de Windows Runtime of door Windows Runtime-onderdelen.

In .NET Framework hebt u standaard toegang tot elk object vanuit meerdere threads, zonder speciale verwerking. U hebt alleen een verwijzing naar het object nodig. In Windows Runtime worden dergelijke objecten agilegenoemd. De meeste Windows Runtime-klassen zijn flexibel, maar een paar klassen zijn niet, en zelfs agile-klassen vereisen mogelijk speciale verwerking.

Waar mogelijk behandelt de Common Language Runtime (CLR) objecten uit andere bronnen, zoals de Windows Runtime, alsof het .NET Framework-objecten zijn:

  • Als het object de IAgileObject interface implementeert of de MarshalingBehaviorAttribute kenmerk heeft met MarshalingType.Agile, behandelt de CLR het als agile.

  • Als CLR een aanroep van de thread kan verwerken naar de threadingcontext van het doelobject, gebeurt dit automatisch en zonder tussenkomst.

  • Als het object het kenmerk MarshalingBehaviorAttribute heeft met MarshalingType.None, biedt de klasse geen marshaling-informatie. De CLR kan de aanroep niet marshalen, dus genereert het een InvalidCastException uitzondering met een bericht dat aangeeft dat het object alleen kan worden gebruikt in de threadingcontext waarin het is gemaakt.

In de volgende secties worden de effecten van dit gedrag op objecten uit verschillende bronnen beschreven.

Objecten van een Windows Runtime-onderdeel dat is geschreven in C# of Visual Basic

Alle typen in het onderdeel die kunnen worden geactiveerd, zijn standaard flexibel.

Opmerking

Flexibiliteit impliceert geen threadveiligheid. In zowel Windows Runtime als .NET Framework zijn de meeste klassen niet threadveilig omdat threadveiligheid prestatiekosten heeft en de meeste objecten nooit worden geopend door meerdere threads. Het is efficiënter om de toegang tot afzonderlijke objecten te synchroniseren (of om threadveilige klassen te gebruiken) alleen indien nodig.

Wanneer u een Windows Runtime-onderdeel maakt, kunt u de standaardwaarde overschrijven. Zie de interface ICustomQueryInterface en de interface IAgileObject.

Objecten uit Windows Runtime

De meeste klassen in Windows Runtime zijn flexibel en de CLR behandelt ze als agile. De documentatie voor deze klassen vermeldt "MarshalingBehaviorAttribute(Agile)" onder de klassekenmerken. De leden van sommige van deze agile-klassen, zoals XAML-besturingselementen, genereren echter uitzonderingen als ze niet worden aangeroepen op de UI-thread. Met de volgende code wordt bijvoorbeeld geprobeerd een achtergrondthread te gebruiken om een eigenschap in te stellen van de knop waarop is geklikt. De eigenschap Inhoud van de knop genereert een uitzondering.

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

U kunt de knop veilig openen met behulp van de eigenschap Dispatcher of de Dispatcher eigenschap van een object dat bestaat in de context van de UI-thread (zoals de pagina waarop de knop zich bevindt). De volgende code maakt gebruik van de methode CoreDispatcher object RunAsync om de aanroep op de UI-thread te verzenden.

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

Opmerking

De eigenschap Dispatcher genereert geen uitzondering wanneer deze wordt aangeroepen vanuit een andere thread.

De levensduur van een Windows Runtime-object dat is gemaakt op de UI-thread, wordt gebonden door de levensduur van de thread. Probeer geen toegang te krijgen tot objecten in een UI-thread nadat het venster is gesloten.

Als u uw eigen besturingselement maakt door een XAML-besturingselement over te nemen of door een set XAML-besturingselementen op te stellen, is uw besturingselement flexibel omdat het een .NET Framework-object is. Als er echter leden van de basisklasse of samenstellende klassen worden aangeroepen, of als u overgenomen leden aanroept, genereren deze leden uitzonderingen wanneer ze worden aangeroepen vanuit een thread, met uitzondering van de UI-thread.

Klassen die niet kunnen worden geïmporteerd

Windows Runtime-klassen die geen marshaling-informatie bieden, hebben het MarshalingBehaviorAttribute kenmerk met MarshalingType.None. De documentatie voor een dergelijke klasse vermeldt "MarshalingBehaviorAttribute(None)" onder de kenmerken.

Met de volgende code maakt u een CameraCaptureUI object op de UI-thread en probeert vervolgens een eigenschap van het object in te stellen op basis van een threadpoolthread. De CLR kan de aanroep niet marshalen en genereert een System.InvalidCastException uitzondering met een bericht dat aangeeft dat het object alleen kan worden gebruikt in de threadingcontext waarin het is gemaakt.

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

In de documentatie voor CameraCaptureUI wordt ook 'ThreadingAttribute(STA)' vermeld onder de kenmerken van de klasse, omdat deze moet worden gemaakt in een context met één thread, zoals de UI-thread.

Als u toegang wilt krijgen tot het CameraCaptureUI--object vanuit een andere thread, kunt u het CoreDispatcher--object voor de UI-thread opslaan en later gebruiken om de aanroep op die thread te verzenden. U kunt de dispatcher ook verkrijgen via een XAML-object, zoals de pagina, zoals wordt weergegeven in de volgende code.

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

Objecten van een Windows Runtime-onderdeel dat is geschreven in C++

Klassen in het onderdeel die kunnen worden geactiveerd, zijn standaard flexibel. C++ biedt echter een aanzienlijke mate van controle over threadingmodellen en marshalinggedrag. Zoals eerder in dit artikel is beschreven, herkent de CLR agile-klassen, probeert marshal-aanroepen wanneer klassen niet flexibel zijn en genereert een System.InvalidCastException uitzondering wanneer een klasse geen marshaling-informatie heeft.

Voor objecten die worden uitgevoerd op de UI-thread en uitzonderingen genereren wanneer ze worden aangeroepen vanuit een andere thread dan de UI-thread, kunt u het CoreDispatcher--object van de UI-thread gebruiken om de oproep te verzenden.

Zie ook

C#-handleiding

Visual Basic Guide