方法: Windows フォーム コントロールのスレッド セーフな呼び出しを行うHow to: Make thread-safe calls to Windows Forms controls

マルチ スレッドは、Windows フォームのアプリのパフォーマンスを向上させることができますが、Windows フォーム コントロールへのアクセスがスレッド セーフでは本質的にはありません。Multithreading can improve the performance of Windows Forms apps, but access to Windows Forms controls isn't inherently thread-safe. マルチ スレッドは、非常に深刻かつ複雑なバグ、コードを公開できます。Multithreading can expose your code to very serious and complex bugs. 2 つまたは複数のスレッドが、コントロールの操作では、コントロールが不整合な状態に強制的に移動でき、競合状態、デッドロック、およびがフリーズやハングすることができます。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. 実装する場合は、マルチ スレッド アプリケーションでは、スレッド セーフな方法で、スレッド間のコントロールを呼び出すことを確認します。If you implement multithreading in your app, be sure to call cross-thread controls in a thread-safe way. 詳細については、次を参照してください。マネージ スレッド処理のベスト プラクティスします。For more information, see Managed threading best practices.

Windows フォーム コントロールをそのコントロールを作成していないスレッドから安全に呼び出すに 2 つの方法はあります。There are two ways to safely call a Windows Forms control from a thread that didn't create that control. 使用することができます、System.Windows.Forms.Control.Invokeコントロールを呼び出し、メイン スレッドで作成したデリゲートを呼び出すメソッド。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. または、実装することができます、System.ComponentModel.BackgroundWorker結果のレポートから、バック グラウンド スレッドで実行される作業を分離するにはイベント ドリブン モデルを使用します。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.

安全でないスレッド間の呼び出しUnsafe cross-thread calls

コントロールを作成していないスレッドから直接呼び出しは安全ではありません。It's unsafe to call a control directly from a thread that didn't create it. 次のコード スニペットは、安全でない呼び出しを示しています、System.Windows.Forms.TextBoxコントロール。The following code snippet illustrates an unsafe call to the System.Windows.Forms.TextBox control. Button1_Clickイベント ハンドラーを作成する新しいWriteTextUnsafeスレッドで、設定、メイン スレッドのTextBox.Textプロパティを直接します。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

Visual Studio デバッガーは、発生させることによってこれらの安全でないスレッドの呼び出しを検出、InvalidOperationExceptionメッセージと共にスレッド間の操作が無効です。コントロール""上で作成されたスレッド以外のスレッドからアクセスします。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. InvalidOperationException常に、Visual Studio のデバッグ時に安全でないスレッド間の呼び出しが発生し、アプリの実行時に発生する可能性があります。The InvalidOperationException always occurs for unsafe cross-thread calls during Visual Studio debugging, and may occur at app runtime. 問題を修正する必要がありますが、設定して、例外を無効にすることができます、Control.CheckForIllegalCrossThreadCallsプロパティをfalseします。You should fix the issue, but you can disable the exception by setting the Control.CheckForIllegalCrossThreadCalls property to false.

スレッド間の安全な呼び出しSafe cross-thread calls

次のコード例は、それを作成していないスレッドから、Windows フォーム コントロールを安全に呼び出す 2 つの方法を示しています。The following code examples demonstrate two ways to safely call a Windows Forms control from a thread that didn't create it:

  1. System.Windows.Forms.Control.Invokeメソッドを呼び出すコントロールをメイン スレッドからデリゲートを呼び出します。The System.Windows.Forms.Control.Invoke method, which calls a delegate from the main thread to call the control.
  2. ASystem.ComponentModel.BackgroundWorkerコンポーネントであり、イベント ドリブン モデルを提供します。A System.ComponentModel.BackgroundWorker component, which offers an event-driven model.

どちらの例では、シミュレートする 2 番目の 1 つのバック グラウンド スレッド スリープは、そのスレッドで実行されている動作します。In both examples, the background thread sleeps for one second to simulate work being done in that thread.

ビルドしてから、.NET Framework アプリとしてこれらの例を実行することができます、C#または Visual Basic のコマンド ライン。You can build and run these examples as .NET Framework apps from the C# or Visual Basic command line. 詳細については、次を参照してください。 csc.exe をまたは(Visual Basic) コマンドラインからビルドします。For more information, see Command-line building with csc.exe or Build from the command line (Visual Basic).

.NET Core 3.0 以降では、ことができますもビルドおよび実行する例では、.NET Core アプリを Windows として .NET Core の Windows フォームのあるフォルダーから <フォルダー名>.csproj プロジェクト ファイル。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.

例:デリゲートで Invoke メソッドを使用します。Example: Use the Invoke method with a delegate

次の例では、Windows フォーム コントロールのスレッド セーフな呼び出しを確保するためのパターンを示します。The following example demonstrates a pattern for ensuring thread-safe calls to a Windows Forms control. これは、クエリをSystem.Windows.Forms.Control.InvokeRequiredコントロールを比較し、プロパティの呼び出し元のスレッド id ですスレッドの ID を作成する。It queries the System.Windows.Forms.Control.InvokeRequired property, which compares the control's creating thread ID to the calling thread ID. スレッド Id が同じ場合は、コントロールを直接呼び出します。If the thread IDs are the same, it calls the control directly. 呼び出しスレッド Id が異なる場合、Control.Invokeコントロールに実際の呼び出し、メイン スレッドからのデリゲート メソッド。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.

SafeCallDelegate設定を有効に、TextBoxコントロールのTextプロパティ。The SafeCallDelegate enables setting the TextBox control's Text property. WriteTextSafeメソッド クエリInvokeRequiredします。The WriteTextSafe method queries InvokeRequired. 場合InvokeRequired返しますtrueWriteTextSafe渡します、SafeCallDelegateInvokeをコントロールには、実際の呼び出しを行うメソッドです。If InvokeRequired returns true, WriteTextSafe passes the SafeCallDelegate to the Invoke method to make the actual call to the control. 場合InvokeRequired返しますfalseWriteTextSafe設定、TextBox.Text直接します。If InvokeRequired returns false, WriteTextSafe sets the TextBox.Text directly. Button1_Clickイベント ハンドラーが新しいスレッドを作成および実行される、WriteTextSafeメソッド。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

例:BackgroundWorker のイベント ハンドラーを使用します。Example: Use a BackgroundWorker event handler

簡単にマルチ スレッドの実装は、System.ComponentModel.BackgroundWorkerイベント ドリブン モデルを使用して、コンポーネント。An easy way to implement multithreading is with the System.ComponentModel.BackgroundWorker component, which uses an event-driven model. バック グラウンド スレッドが、BackgroundWorker.DoWorkイベントで、メイン スレッドと対話しません。The background thread runs the BackgroundWorker.DoWork event, which doesn't interact with the main thread. メイン スレッドが実行され、BackgroundWorker.ProgressChangedBackgroundWorker.RunWorkerCompletedイベント ハンドラーは、メイン スレッドのコントロールを呼び出すことができます。The main thread runs the BackgroundWorker.ProgressChanged and BackgroundWorker.RunWorkerCompleted event handlers, which can call the main thread's controls.

使用して、スレッド セーフな呼び出しを行うBackgroundWorker、操作を実行するバック グラウンド スレッドでメソッドを作成し、バインドするに、はDoWorkイベント。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. バック グラウンド作業の結果を報告するメイン スレッドで別のメソッドを作成しにバインド、ProgressChangedまたはRunWorkerCompletedイベント。Create another method in the main thread to report the results of the background work, and bind it to the ProgressChanged or RunWorkerCompleted event. バック グラウンド スレッドを開始するには、呼び出すBackgroundWorker.RunWorkerAsyncします。To start the background thread, call BackgroundWorker.RunWorkerAsync.

この例では、RunWorkerCompletedイベント ハンドラーを設定する、TextBoxコントロールのTextプロパティ。The example uses the RunWorkerCompleted event handler to set the TextBox control's Text property. 使用例について、ProgressChangedイベントを参照してください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

関連項目See also