Mantenere reattivo il thread dell'interfaccia utente

L’utente si aspetta che un’app sia reattiva durante l’esecuzione di calcoli, indipendentemente dal tipo di computer in uso. Le implicazioni sono diverse per app differenti. Per alcune si tratta di fornire risultati fisici più efficienti, caricare i dati dal disco o dal Web più velocemente, visualizzare scene complesse e passare da una pagina all'altra rapidamente, trovare le direzioni in un attimo o elaborare velocemente i dati. Indipendentemente dal tipo di calcolo, l'utente vuole che l'app risponda all'input ed elimini le istanze che sembrano non rispondere durante le fasi di elaborazione.

La tua app è guidata dagli eventi, il che significa che il codice viene eseguito in risposta a un evento e quindi rimane inattivo fino all'evento successivo. Il codice di piattaforma per l'interfaccia utente (layout, input, generazione di eventi e così via) e il codice della tua app per l'interfaccia utente vengono eseguiti sullo stesso thread dell'interfaccia utente. È possibile eseguire una sola istruzione alla volta su tale thread, quindi se il codice della tua app richiede troppo tempo per elaborare un evento, il framework non può eseguire il layout o generare nuovi eventi che rappresentano le interazioni dell'utente. La velocità di risposta dell'app è correlata alla disponibilità del thread dell'interfaccia utente per le operazioni di elaborazione.

Devi usare il thread dell'interfaccia utente per eseguire quasi tutte le modifiche al thread dell'interfaccia utente, inclusi la creazione di tipi dell'interfaccia utente e l'accesso ai relativi membri. Non puoi aggiornare l’interfaccia utente da un thread in background, ma puoi inviare un messaggio con CoreDispatcher.RunAsync per ottenere l’esecuzione del codice in tale posizione.

Nota: l'unica eccezione è la presenza di un thread di rendering separato che può applicare modifiche dell'interfaccia utente che non influiscono sulla modalità di gestione dell'input o sul layout di base. Ad esempio, molte animazioni e transizioni che non influiscono sul layout possono essere eseguite in questo thread di rendering.

Ritardare la creazione di istanze degli elementi

Alcune delle fasi più lente in un'app possono essere l'avvio e il cambio di visualizzazioni. Non lavorare più del necessario per visualizzare l'interfaccia utente che l'utente vede inizialmente. Ad esempio, non creare l'interfaccia utente per elementi di interfaccia utente a visualizzazione progressiva e per il contenuto di popup.

Le code CoreDispatcher.RunIdleAsync collaborano con il thread dell'interfaccia utente per eseguire le elaborazioni quando non è occupato.

Usare le API asincrone

Per mantenere la tua app reattiva, la piattaforma offre versioni asincrone di molte API. Un'API asincrona garantisce che il thread di esecuzione attivo non si blocchi mai per molto tempo. Quando chiami un'API dal thread dell'interfaccia utente, usa la versione asincrona, se disponibile. Per altre info sulla programmazione con modelli async, vedi Programmazione asincrona o Chiamare API asincrone in C# o Visual Basic.

Offload di operazioni nei thread in background

Scrivi gestori eventi che restituiscono il controllo rapidamente. Nei casi in cui è necessario eseguire un numero non irrilevante di operazioni, pianificane l'esecuzione in un thread in background e restituisci il controllo.

Puoi pianificare le operazioni in modo asincrono tramite l’operatore await in C#, l’operatore Await in Visual Basic o i delegati in C++. Questo però non garantisce che le operazioni pianificate vengano eseguite in un thread in background. Molte delle API della piattaforma UWP (Universal Windows Platform) pianificano automaticamente le operazioni nel thread in background, ma se chiami il codice dell’app solo con await o un delegato, tale delegato o metodo viene eseguito nel thread dell’interfaccia utente. Devi specificare in modo esplicito quando eseguire il codice dell'app in un thread in background. In C# e Visual Basic puoi ottenere questo risultato passando il codice a Task.Run.

Ricorda che gli elementi dell'interfaccia utente sono accessibili solo dal thread dell'interfaccia utente. Usa il thread dell’interfaccia utente per accedere agli elementi dell’interfaccia utente prima di avviare le operazioni in background e/o usa CoreDispatcher.RunAsync o CoreDispatcher.RunIdleAsync sul thread in background.

Un esempio di operazione che puoi eseguire in un thread in background è il calcolo dell'intelligenza artificiale del computer in un gioco. L'esecuzione del codice che calcola la mossa successiva del computer può richiedere molto tempo.

public class AsyncExample
{
    private async void NextMove_Click(object sender, RoutedEventArgs e)
    {
        // The await causes the handler to return immediately.
        await System.Threading.Tasks.Task.Run(() => ComputeNextMove());
        // Now update the UI with the results.
        // ...
    }

    private async System.Threading.Tasks.Task ComputeNextMove()
    {
        // Perform background work here.
        // Don't directly access UI elements from this method.
    }
}
Public Class AsyncExample
    ' ...
    Private Async Sub NextMove_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
        Await Task.Run(Function() ComputeNextMove())
        ' update the UI with results
    End Sub

    Private Async Function ComputeNextMove() As Task
        ' ...
    End Function
    ' ...
End Class

In questo esempio, il gestore NextMove_Click restituisce il controllo in corrispondenza di await per mantenere reattivo il thread dell’interfaccia utente. Ma l’esecuzione riprenderà in tale gestore dopo il completamento di ComputeNextMove (la cui esecuzione avviene in un thread in background). Il resto del codice nel gestore aggiorna l'interfaccia utente con i risultati.

Nota: sono disponibili anche API ThreadPool e ThreadPoolTimer per la piattaforma UWP, che possono essere usate per scenari analoghi. Per altre info, vedi Threading e programmazione asincrona.