C# の System.InvalidOperationException の解決: 「無効なスレッド間操作: コントロール "..." は、それを作成したスレッド以外のスレッドからアクセスされています。」

エラーコード

// Form1.cs
public partial class Form1 : Form
{
    
    
    public Form1()
    {
    
    
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
    
    
        Task.Run(() => // 模拟另一线程
        {
    
    
            // 在另一线程内做一些耗时操作
            //...
            string result = "...";
            
            // 输出结果(将引发异常)
            textBox1.Text = result; // System.InvalidOperationException
        });
    }
}

例外情報

中文:
System.InvalidOperationException:“线程间操作无效: 从不是创建控件“textBox1”的线程访问它。”

英文:
System.InvalidOperationException: Cross-thread operation not valid: Control 'textBox1' accessed from a thread other than the thread it was created on.

解決

方法 1 (非推奨): クロススレッド チェックを無効にする

使用Control.CheckForIllegalCrossThreadCalls = false;

例えば:

// Form1.cs
public partial class Form1 : Form
{
    
    
    public Form1()
    {
    
    
        InitializeComponent();
        Control.CheckForIllegalCrossThreadCalls = false; // 禁用跨线程检查
    }

    private void button1_Click(object sender, EventArgs e)
    {
    
    
        Task.Run(() => // 模拟另一线程
        {
    
    
            // 在另一线程内做一些耗时操作
            //...
            string result = "...";
            
            // 输出结果(不会引发异常)
            textBox1.Text = result;
        });
    }
}

方法 2: デリゲートを使用する

クロススレッドを禁止するコードを実行するには、Control基本クラスのInvokeまたはメソッドを使用します。BeginInvoke

例えば:

// Form1.cs
public partial class Form1 : Form
{
    
    
    public Form1()
    {
    
    
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
    
    
        Task.Run(() => // 模拟另一线程
        {
    
    
            // 在另一线程内做一些耗时操作
            //...
            string result = "...";
            
            // 输出结果(不会引发异常)
            this.Invoke(() =>
            {
    
    
                textBox1.Text = result;
            });
        });
    }
}

パラメータを使用して次のものを渡します。

// Form1.cs
public partial class Form1 : Form
{
    
    
    public Form1()
    {
    
    
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
    
    
        Task.Run(() => // 模拟另一线程
        {
    
    
            // 在另一线程内做一些耗时操作
            //...
            string result = "...";
            
            // 输出结果(不会引发异常)
            this.Invoke((string text, int length) =>
            {
    
    
                textBox1.Text = $"Text={
      
      text}, Length={
      
      length}";
            }, result, result.Length);
        });
    }
}

ラムダ式を使わずに書くと以下のようになります。

// Form1.cs
public partial class Form1 : Form
{
    
    
    public Form1()
    {
    
    
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
    
    
        Task.Run(() => // 模拟另一线程
        {
    
    
            // 在另一线程内做一些耗时操作
            //...
            string result = "...";
            
            // 输出结果(不会引发异常)
            this.Invoke(new SetTextEvent(SetText), result, result.Length);
        });
    }

    private void SetText(string text, int length)
    {
    
    
        textBox1.Text = $"Text={
      
      text}, Length={
      
      length}";
    }

    private delegate void SetTextEvent(string text, int length);
}

BeginInvokeメソッドの使用は、Invokeメソッドの使用と似ています。2 つの違いは、Invokeこのメソッドはメイン スレッド (ここでは GUI スレッドを指します) で作業を完了するのに対し、BeginInvoke新しいスレッドで作業を完了することです (もちろん、これはエラーを報告しません)。

方法 3: BackgroundWorker を使用して、時間のかかるプロセス全体を完了します (GUI の変更だけではありません)。

例えば:

// Form1.cs
public partial class Form1 : Form
{
    
    
    public Form1()
    {
    
    
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
    
    
        using (BackgroundWorker backgroundWorker = new BackgroundWorker())
        {
    
    
            backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);
            backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker_RunWorkerCompleted);
            backgroundWorker.RunWorkerAsync(); // 可以传入参数
        }

        // 后续过程(不会被 BackgroundWorker 阻塞,即不会等待其完毕再执行)
        // ...
    }

    private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
    {
    
    
        //e.Argument // 可以取得传入的参数

        // 模拟耗时操作
        //...
        string result = "...";
        
        // 传递结果
        e.Result = result;
    }

    private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
    
    
        textBox1.Text = e.Result.ToString(); // 输出结果
    }
}

進行状況のフィードバックとキャンセルのサポートを含む別の例:
私の他の記事を参照してください: C# BackgroundWorker の単純な例

参考文献

おすすめ

転載: blog.csdn.net/xzqsr2011/article/details/131454098