.NET Framework Accesso ai controlli del modulo da altri thread


Esempio

Se si desidera modificare un attributo di un controllo come una casella di testo o un'etichetta da un altro thread rispetto al thread della GUI che ha creato il controllo, sarà necessario richiamarlo altrimenti si potrebbe ottenere un messaggio di errore che indica:

"Operazione cross-thread non valida: controllo 'control_name' accessibile da un thread diverso dal thread su cui è stato creato."

L'uso di questo codice di esempio su un modulo system.windows.forms genera un'eccezione con quel messaggio:

private void button4_Click(object sender, EventArgs e)
{
    Thread thread = new Thread(updatetextbox);
    thread.Start();
}

private void updatetextbox()
{
    textBox1.Text = "updated"; // Throws exception
}

Invece quando si desidera modificare il testo di una casella di testo all'interno di un thread che non lo possiede, utilizzare Control.Invoke o Control.BeginInvoke. È inoltre possibile utilizzare Control.InvokeRequired per verificare se è necessario il richiamo del controllo.

private void updatetextbox()
{
    if (textBox1.InvokeRequired)
        textBox1.BeginInvoke((Action)(() => textBox1.Text = "updated"));
    else
        textBox1.Text = "updated";
}

Se è necessario farlo spesso, è possibile scrivere un'estensione per oggetti richiamabili per ridurre la quantità di codice necessaria per effettuare questo controllo:

public static class Extensions
{
    public static void BeginInvokeIfRequired(this ISynchronizeInvoke obj, Action action)
    {
        if (obj.InvokeRequired)
            obj.BeginInvoke(action, new object[0]);
        else
            action();
    }
}

E l'aggiornamento della casella di testo da qualsiasi thread diventa un po 'più semplice:

private void updatetextbox()
{
    textBox1.BeginInvokeIfRequired(() => textBox1.Text = "updated");
}

Tenere presente che Control.BeginInvoke come in questo esempio è asincrono, il che significa che il codice proveniente dopo una chiamata a Control.BeginInvoke può essere eseguito immediatamente dopo, indipendentemente dal fatto che il delegato passato sia stato eseguito o meno.

Se devi essere sicuro che textBox1 sia aggiornato prima di continuare, usa invece Control.Invoke, che bloccherà il thread chiamante fino a quando il tuo delegato non sarà stato eseguito. Si noti che questo approccio può rallentare notevolmente il codice se si effettuano molte chiamate invocate e si noti che l'applicazione si bloccherà se il thread della GUI è in attesa che il thread chiamante completi o rilasci una risorsa trattenuta.