Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Questo articolo illustra il modo in cui .NET Framework gestisce le chiamate dal codice C# e Visual Basic agli oggetti forniti da Windows Runtime o dai componenti Windows Runtime.
In .NET Framework è possibile accedere a qualsiasi oggetto da più thread per impostazione predefinita, senza una gestione speciale. Tutto quello che serve è un riferimento all'oggetto . In Windows Runtime tali oggetti sono chiamati Agile. La maggior parte delle classi di Windows Runtime è agile, ma alcune classi non sono e anche le classi Agile possono richiedere una gestione speciale.
Laddove possibile, Common Language Runtime (CLR) gestisce oggetti di altre origini, ad esempio Windows Runtime, come se fossero oggetti .NET Framework:
Se l'oggetto implementa l'interfaccia IAgileObject, o dispone dell'attributo MarshalingBehaviorAttribute con MarshalingType.Agile, il CLR lo considera agile.
Se il CLR è in grado di gestire una chiamata dal thread in cui è stata effettuata al contesto di threading dell'oggetto di destinazione, lo fa in modo trasparente.
Se l'oggetto dispone dell'attributo MarshalingBehaviorAttribute con MarshalingType.None, la classe non fornisce informazioni di marshaling. Il CLR non è in grado di eseguire il marshalling della chiamata, pertanto genera un'eccezione InvalidCastException con un messaggio che indica che l'oggetto può essere utilizzato solo nel contesto di threading in cui è stato creato.
Le sezioni seguenti descrivono gli effetti di questo comportamento sugli oggetti provenienti da varie origini.
Oggetti di un componente Windows Runtime scritto in C# o Visual Basic
Tutti i tipi del componente che possono essere attivati sono agili per impostazione predefinita.
Annotazioni
L'agilità non implica la sicurezza dei thread. Sia in Windows Runtime che in .NET Framework, la maggior parte delle classi non è thread-safe perché thread safety ha un costo delle prestazioni e la maggior parte degli oggetti non è mai accessibile da più thread. È più efficiente sincronizzare l'accesso a singoli oggetti (o usare classi thread-safe) solo se necessario.
Quando si crea un componente Windows Runtime, è possibile eseguire l'override del valore predefinito. Vedere l'interfaccia ICustomQueryInterface e l'interfaccia IAgileObject.
Oggetti di Windows Runtime
La maggior parte delle classi in Windows Runtime è agile e il CLR le considera agili. La documentazione di queste classi elenca "MarshalingBehaviorAttribute(Agile)" tra gli attributi della classe. Tuttavia, i membri di alcune di queste classi Agile, ad esempio i controlli XAML, generano eccezioni se non vengono chiamati sul thread dell'interfaccia utente. Ad esempio, il codice seguente tenta di usare un thread in background per impostare una proprietà del pulsante su cui è stato fatto clic. La proprietà content del pulsante
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
È possibile accedere al pulsante in modo sicuro usando la relativa proprietà Dispatcher o la Dispatcher proprietà di qualsiasi oggetto presente nel contesto del thread dell'interfaccia utente, ad esempio la pagina in cui si trova il pulsante. Il codice seguente utilizza il metodo RunAsync dell’oggetto CoreDispatcher per eseguire la chiamata sul thread UI.
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
Annotazioni
La Dispatcher proprietà non genera un'eccezione quando viene chiamata da un altro thread.
La durata di un oggetto Windows Runtime creato nel thread dell'interfaccia utente è vincolata dalla durata del thread. Non tentare di accedere agli oggetti in un thread dell'interfaccia utente dopo la chiusura della finestra.
Se crei un controllo personalizzato ereditando un controllo XAML o componendo un set di controlli XAML, il controllo è agile perché si tratta di un oggetto .NET Framework. Tuttavia, se chiama i membri della sua classe di base o delle classi componenti, o se si chiamano i membri ereditati, tali membri genereranno delle eccezioni quando vengono chiamati da qualsiasi thread, ad eccezione del thread dell'interfaccia utente.
Classi che non possono essere sottoposte a serializzazione
Le classi di Windows Runtime che non forniscono informazioni sul marshalling hanno l'attributo MarshalingBehaviorAttribute con MarshalingType.None. La documentazione per una classe di questo tipo elenca "MarshalingBehaviorAttribute(None)" tra i relativi attributi.
Il codice seguente crea un oggetto CameraCaptureUI nel thread dell'interfaccia utente e quindi tenta di impostare una proprietà dell'oggetto da un thread del pool di thread. Il CLR non è in grado di eseguire il marshalling della chiamata e genera un'eccezione System.InvalidCastException con un messaggio che indica che l'oggetto può essere utilizzato solo nel contesto di threading in cui è stato creato.
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 documentazione di CameraCaptureUI elenca anche "ThreadingAttribute(STA)" tra gli attributi della classe, perché deve essere creata in un contesto a thread singolo, ad esempio il thread dell'interfaccia utente.
Se si desidera accedere all'oggetto CameraCaptureUI da un altro thread, è possibile memorizzare nella cache l'oggetto CoreDispatcher per il thread dell'UI e usarlo successivamente per inoltrare la chiamata su quel thread. In alternativa, puoi ottenere il dispatcher da un oggetto XAML, ad esempio la pagina, come illustrato nel codice seguente.
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
Oggetti di un componente Windows Runtime scritto in C++
Per impostazione predefinita, le classi nel componente che possono essere attivate sono agili. Tuttavia, C++ consente una quantità significativa di controllo sui modelli di threading e sul comportamento di marshalling. Come descritto in precedenza in questo articolo, il CLR riconosce le classi agili, tenta di eseguire il marshalling delle chiamate quando le classi non sono agili e genera un'eccezione System.InvalidCastException quando una classe non dispone di informazioni di marshalling.
Per gli oggetti eseguiti nel thread dell'interfaccia utente, che generano eccezioni quando vengono chiamati da un thread diverso, si può utilizzare l'oggetto CoreDispatcher del thread dell'interfaccia utente per inviare la chiamata.