Procedura: Effettuare chiamate thread-safe a controlli Windows FormHow to: Make thread-safe calls to Windows Forms controls

Il multithreading può migliorare le prestazioni delle app di Windows Form, ma l'accesso ai controlli Windows Form non è intrinsecamente thread-safe.Multithreading can improve the performance of Windows Forms apps, but access to Windows Forms controls isn't inherently thread-safe. Il multithreading può esporre il codice a bug seri e complessi.Multithreading can expose your code to very serious and complex bugs. Due o più thread, la modifica di un controllo può forzare il controllo in uno stato incoerente e causare condizioni di competizione, deadlock e si blocca o si blocca.Two or more threads manipulating a control can force the control into an inconsistent state and lead to race conditions, deadlocks, and freezes or hangs. Se si implementa il multithreading nell'app, assicurarsi di chiamare i controlli di cross-thread in modo thread-safe.If you implement multithreading in your app, be sure to call cross-thread controls in a thread-safe way. Per altre informazioni, vedere procedure consigliate di threading gestito.For more information, see Managed threading best practices.

Esistono due modi per chiamare in modo sicuro un controllo Windows Form da un thread che non sono stati creati tale controllo.There are two ways to safely call a Windows Forms control from a thread that didn't create that control. È possibile usare il System.Windows.Forms.Control.Invoke metodo da chiamare un delegato creato nel thread principale, che a sua volta chiama il controllo.You can use the System.Windows.Forms.Control.Invoke method to call a delegate created in the main thread, which in turn calls the control. In alternativa, è possibile implementare un System.ComponentModel.BackgroundWorker, che usa un modello basato su eventi per separare attività nel thread in background dal servizio di report sui risultati.Or, you can implement a System.ComponentModel.BackgroundWorker, which uses an event-driven model to separate work done in the background thread from reporting on the results.

Chiamate cross-thread non sicureUnsafe cross-thread calls

Non è sicuro chiamare un controllo direttamente da un thread che non è stato creato.It's unsafe to call a control directly from a thread that didn't create it. Il frammento di codice seguente viene illustrata una chiamata non sicura per la System.Windows.Forms.TextBox controllo.The following code snippet illustrates an unsafe call to the System.Windows.Forms.TextBox control. Il Button1_Click crea un nuovo gestore eventi WriteTextUnsafe thread, che imposta il thread principale TextBox.Text proprietà direttamente.The Button1_Click event handler creates a new WriteTextUnsafe thread, which sets the main thread's TextBox.Text property directly.

private void Button1_Click(object sender, EventArgs e)
{
    thread2 = new Thread(new ThreadStart(WriteTextUnsafe));
    thread2.Start();
}
private void WriteTextUnsafe()
{
    textBox1.Text = "This text was set unsafely.";
}
Private Sub Button1_Click(ByVal sender As Object, e As EventArgs) Handles Button1.Click
    Thread2 = New Thread(New ThreadStart(AddressOf WriteTextUnsafe))
    Thread2.Start()
End Sub

Private Sub WriteTextUnsafe()
    TextBox1.Text = "This text was set unsafely."
End Sub

Il debugger di Visual Studio rileva queste chiamate di thread non sicuro mediante la generazione di un' InvalidOperationException con il messaggio, operazione Cross-thread non è valido. Controllo "" accedere da un thread diverso da quello in cui è stato creato.The Visual Studio debugger detects these unsafe thread calls by raising an InvalidOperationException with the message, Cross-thread operation not valid. Control "" accessed from a thread other than the thread it was created on. Il InvalidOperationException sempre si verifica per chiamate cross-thread non sicure durante il debug di Visual Studio e possono verificarsi in fase di esecuzione di app.The InvalidOperationException always occurs for unsafe cross-thread calls during Visual Studio debugging, and may occur at app runtime. È consigliabile risolvere il problema, ma è possibile disabilitare l'eccezione impostando il Control.CheckForIllegalCrossThreadCalls proprietà false.You should fix the issue, but you can disable the exception by setting the Control.CheckForIllegalCrossThreadCalls property to false.

Chiamate cross-thread-safeSafe cross-thread calls

Gli esempi di codice seguenti illustrano due modi per chiamare in modo sicuro un controllo Windows Form da un thread che non è stato creato:The following code examples demonstrate two ways to safely call a Windows Forms control from a thread that didn't create it:

  1. Il System.Windows.Forms.Control.Invoke metodo, che chiama un delegato dal thread principale per chiamare il controllo.The System.Windows.Forms.Control.Invoke method, which calls a delegate from the main thread to call the control.
  2. Oggetto System.ComponentModel.BackgroundWorker componente, che offre un modello basato sugli eventi.A System.ComponentModel.BackgroundWorker component, which offers an event-driven model.

In entrambi gli esempi, la disattiva di thread in background per un secondo simulare di lavoro viene eseguita in tale thread.In both examples, the background thread sleeps for one second to simulate work being done in that thread.

È possibile compilare ed eseguire questi esempi di come le app .NET Framework dal C# o riga di comando di Visual Basic.You can build and run these examples as .NET Framework apps from the C# or Visual Basic command line. Per altre informazioni, vedere della riga di comando edificio con csc.exe oppure compilazione dalla riga di comando (Visual Basic).For more information, see Command-line building with csc.exe or Build from the command line (Visual Basic).

A partire da .NET Core 3.0, è possibile anche creare ed eseguire gli esempi come le app .NET Core di Windows da una cartella che dispone di un form di Windows di .NET Core <nome cartella > csproj file di progetto.Starting with .NET Core 3.0, you can also build and run the examples as Windows .NET Core apps from a folder that has a .NET Core Windows Forms <folder name>.csproj project file.

Esempio: Usare il metodo Invoke con un delegatoExample: Use the Invoke method with a delegate

Nell'esempio seguente illustra un modello per garantire chiamate thread-safe a un controllo Windows Form.The following example demonstrates a pattern for ensuring thread-safe calls to a Windows Forms control. Viene eseguita una query di System.Windows.Forms.Control.InvokeRequired proprietà, che confronta il controllo della creazione di ID del thread per l'ID del thread chiamante.It queries the System.Windows.Forms.Control.InvokeRequired property, which compares the control's creating thread ID to the calling thread ID. Se gli ID di thread sono uguali, viene chiamato direttamente il controllo.If the thread IDs are the same, it calls the control directly. Se l'ID del thread sono diversi, chiama il Control.Invoke metodo con un delegato dal thread principale, che semplifica la chiamata effettiva al controllo.If the thread IDs are different, it calls the Control.Invoke method with a delegate from the main thread, which makes the actual call to the control.

Il SafeCallDelegate attiva un'impostazione di TextBox del controllo Text proprietà.The SafeCallDelegate enables setting the TextBox control's Text property. Il WriteTextSafe query di metodo InvokeRequired.The WriteTextSafe method queries InvokeRequired. Se InvokeRequired restituisce true, WriteTextSafe passa il SafeCallDelegate per il Invoke metodo per eseguire la chiamata effettiva al controllo.If InvokeRequired returns true, WriteTextSafe passes the SafeCallDelegate to the Invoke method to make the actual call to the control. Se InvokeRequired restituisce false, WriteTextSafe imposta la TextBox.Text direttamente.If InvokeRequired returns false, WriteTextSafe sets the TextBox.Text directly. Il Button1_Click gestore dell'evento viene creato il nuovo thread e viene eseguito il WriteTextSafe (metodo).The Button1_Click event handler creates the new thread and runs the WriteTextSafe method.

using System;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;

public class InvokeThreadSafeForm : Form
{
    private delegate void SafeCallDelegate(string text);
    private Button button1;
    private TextBox textBox1;
    private Thread thread2 = null;

    [STAThread]
    static void Main()
    {
        Application.SetCompatibleTextRenderingDefault(false);
        Application.EnableVisualStyles();
        Application.Run(new InvokeThreadSafeForm());
    }
    public InvokeThreadSafeForm()
    {
        button1 = new Button
        {
            Location = new Point(15, 55),
            Size = new Size(240, 20),
            Text = "Set text safely"
        };
        button1.Click += new EventHandler(Button1_Click);
        textBox1 = new TextBox
        {
            Location = new Point(15, 15),
            Size = new Size(240, 20)
        };
        Controls.Add(button1);
        Controls.Add(textBox1);
    }

    private void Button1_Click(object sender, EventArgs e)
    {
        thread2 = new Thread(new ThreadStart(SetText));
        thread2.Start();
        Thread.Sleep(1000);
    }

    private void WriteTextSafe(string text)
    {
        if (textBox1.InvokeRequired)
        {
            var d = new SafeCallDelegate(WriteTextSafe);
            Invoke(d, new object[] { text });
        }
        else
        {
            textBox1.Text = text;
        }
    }

    private void SetText()
    {
        WriteTextSafe("This text was set safely.");
    }
}
Imports System.Drawing
Imports System.Threading
Imports System.Windows.Forms

Public Class InvokeThreadSafeForm : Inherits Form

    Public Shared Sub Main()
        Application.SetCompatibleTextRenderingDefault(False)
        Application.EnableVisualStyles()
        Dim frm As New InvokeThreadSafeForm()
        Application.Run(frm)
    End Sub

    Dim WithEvents Button1 As Button
    Dim TextBox1 As TextBox
    Dim Thread2 as Thread = Nothing

    Delegate Sub SafeCallDelegate(text As String)

    Private Sub New()
        Button1 = New Button()
        With Button1
            .Location = New Point(15, 55)
            .Size = New Size(240, 20)
            .Text = "Set text safely"
        End With
        TextBox1 = New TextBox()
        With TextBox1
            .Location = New Point(15, 15)
            .Size = New Size(240, 20)
        End With
        Controls.Add(Button1)
        Controls.Add(TextBox1)
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Thread2 = New Thread(New ThreadStart(AddressOf SetText))
        Thread2.Start()
        Thread.Sleep(1000)
    End Sub

    Private Sub WriteTextSafe(text As String)
        If TextBox1.InvokeRequired Then
            Dim d As New SafeCallDelegate(AddressOf SetText)
            Invoke(d, New Object() {text})
        Else
            TextBox1.Text = text
        End If
    End Sub

    Private Sub SetText()
        WriteTextSafe("This text was set safely.")
    End Sub
End Class

Esempio: Usare un gestore dell'evento BackgroundWorkerExample: Use a BackgroundWorker event handler

Un modo semplice per implementare il multithreading è con il System.ComponentModel.BackgroundWorker componente, che usa un modello basato su eventi.An easy way to implement multithreading is with the System.ComponentModel.BackgroundWorker component, which uses an event-driven model. Il thread in background viene eseguito il BackgroundWorker.DoWork evento, che non interagisce con il thread principale.The background thread runs the BackgroundWorker.DoWork event, which doesn't interact with the main thread. Il thread principale viene eseguito il BackgroundWorker.ProgressChanged e BackgroundWorker.RunWorkerCompleted gestori eventi, che è possono chiamare i controlli del thread principale.The main thread runs the BackgroundWorker.ProgressChanged and BackgroundWorker.RunWorkerCompleted event handlers, which can call the main thread's controls.

Per effettuare una chiamata thread-safe utilizzando BackgroundWorker, creare un metodo nel thread in background per svolgere il lavoro e associarlo al DoWork evento.To make a thread-safe call by using BackgroundWorker, create a method in the background thread to do the work, and bind it to the DoWork event. Creare un altro metodo nel thread principale per riportare i risultati delle attività in background e associarlo al ProgressChanged o RunWorkerCompleted evento.Create another method in the main thread to report the results of the background work, and bind it to the ProgressChanged or RunWorkerCompleted event. Per avviare il thread in background, chiamare BackgroundWorker.RunWorkerAsync.To start the background thread, call BackgroundWorker.RunWorkerAsync.

L'esempio Usa la RunWorkerCompleted gestore dell'evento per impostare il TextBox del controllo Text proprietà.The example uses the RunWorkerCompleted event handler to set the TextBox control's Text property. Per indicazioni su come usare il ProgressChanged eventi, vedere BackgroundWorker.For an example using the ProgressChanged event, see BackgroundWorker.

using System;
using System.ComponentModel;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;

public class BackgroundWorkerForm : Form
{
    private BackgroundWorker backgroundWorker1;
    private Button button1;
    private TextBox textBox1;

    [STAThread]
    static void Main()
    {
        Application.SetCompatibleTextRenderingDefault(false);
        Application.EnableVisualStyles();
        Application.Run(new BackgroundWorkerForm());
    }
    public BackgroundWorkerForm()
    {
        backgroundWorker1 = new BackgroundWorker();
        backgroundWorker1.DoWork += new DoWorkEventHandler(BackgroundWorker1_DoWork);
        backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(BackgroundWorker1_RunWorkerCompleted);
        button1 = new Button
        {
            Location = new Point(15, 55),
            Size = new Size(240, 20),
            Text = "Set text safely with BackgroundWorker"
        };
        button1.Click += new EventHandler(Button1_Click);
        textBox1 = new TextBox
        {
            Location = new Point(15, 15),
            Size = new Size(240, 20)
        };
        Controls.Add(button1);
        Controls.Add(textBox1);
    }
    private void Button1_Click(object sender, EventArgs e)
    {
        backgroundWorker1.RunWorkerAsync();
    }

    private void BackgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        // Sleep 2 seconds to emulate getting data.
        Thread.Sleep(2000);
        e.Result = "This text was set safely by BackgroundWorker.";
    }

    private void BackgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        textBox1.Text = e.Result.ToString();
    }
}
Imports System.ComponentModel
Imports System.Drawing
Imports System.Threading
Imports System.Windows.Forms

Public Class BackgroundWorkerForm : Inherits Form

    Public Shared Sub Main()
        Application.SetCompatibleTextRenderingDefault(False)
        Application.EnableVisualStyles()
        Dim frm As New BackgroundWorkerForm()
        Application.Run(frm)
    End Sub

    Dim WithEvents BackgroundWorker1 As BackgroundWorker
    Dim WithEvents Button1 As Button
    Dim TextBox1 As TextBox

    Private Sub New()
        BackgroundWorker1 = New BackgroundWorker()
        Button1 = New Button()
        With Button1
            .Text = "Set text safely with BackgroundWorker"
            .Location = New Point(15, 55)
            .Size = New Size(240, 20)
        End With
        TextBox1 = New TextBox()
        With TextBox1
            .Location = New Point(15, 15)
            .Size = New Size(240, 20)
        End With
        Controls.Add(Button1)
        Controls.Add(TextBox1)
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        BackgroundWorker1.RunWorkerAsync()
    End Sub

    Private Sub BackgroundWorker1_DoWork(sender As Object, e As DoWorkEventArgs) _
     Handles BackgroundWorker1.DoWork
        ' Sleep 2 seconds to emulate getting data.
        Thread.Sleep(2000)
        e.Result = "This text was set safely by BackgroundWorker."
    End Sub

    Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) _
     Handles BackgroundWorker1.RunWorkerCompleted
        textBox1.Text = e.Result.ToString()
    End Sub
End Class

Vedere ancheSee also