エラーコード
// 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 の単純な例