多线程中使用CheckForIllegalCrossThreadCalls = false访问窗口

在多线程的Windows Forms应用中,直接从非UI线程访问UI控件会导致异常。可以通过在窗口构造函数或特定控件上设置CheckForIllegalCrossThreadCalls为false来禁用检查,但这可能导致线程安全问题。微软建议在调试期间遇到此问题时进行修复,以避免线程不安全的访问。

在多线程程序中,新创建的线程不能访问UI线程创建的窗口控件,如果需要访问窗口中的控件,可以在窗口构造函数中将CheckForIllegalCrossThreadCalls设置为 false

public Form1()
{
    InitializeComponent();
    CheckForIllegalCrossThreadCalls = false;
}

 

也可以针对某一控件进行设置,例如:

    TextBox.CheckForIllegalCrossThreadCalls = false;

 

MSDN说明:

如何:对 Windows 窗体控件进行线程安全调用

 

使用多线程提高 Windows 窗体应用程序的性能时,必须注意以线程安全方式调用控件。

示例

访问 Windows 窗体控件本质上不是线程安全的。如果有两个或多个线程操作某一控件的状态,则可能会迫使该控件进入一种不一致的状态。还可能出现其他与线程相关的 bug,包括争用情况和死锁。确保以线程安全方式访问控件非常重要。

.NET Framework 有助于在以非线程安全方式访问控件时检测到这一问题。在调试器中运行应用程序时,如果创建某控件的线程之外的其他线程试图调用该控件,则调试器会引发一个 InvalidOperationException,并提示消息:“从不是创建控件 control name 的线程访问它。”

此异常在调试期间和运行时的某些情况下可靠地发生。强烈建议您在显示此错误信息时修复此问题。在调试以 .NET Framework 2.0 版之前的 .NET Framework 编写的应用程序时,可能会出现此异常。

可以通过将 CheckForIllegalCrossThreadCalls 属性的值设置为 false 来禁用此异常。这会使控件以与在 Visual Studio 2003 下相同的方式运行。

在 C# WinForms 应用程序中,使用多线程可提高应用程序的响应速度和性能,特别是处理耗时操作时。以下是常见的多线程实现方法及相关技术要点: ### 使用 Thread 类 Thread 类是 .NET 中用于创建和控制线程的基础类。示例代码如下: ```csharp using System; using System.Threading; using System.Windows.Forms; namespace ThreadExample { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { Thread objThread = new Thread(new ThreadStart(ActionMethod)); objThread.Name = "子线程"; objThread.Start(); } private void ActionMethod() { // 这里是子线程执行的操作 } } } ``` 不过,新创建的线程不能直接访问 UI 线程创建的窗口控件。若要访问窗口中的控件,可在窗口构造函数中将 `CheckForIllegalCrossThreadCalls` 设置为 `false`: ```csharp public Form1() { InitializeComponent(); CheckForIllegalCrossThreadCalls = false; // 这里要加上这句否则报异常 } ``` 但这种方法不推荐在生产环境使用,因为它会绕过线程安全检查,可能导致程序不稳定 [^4]。 ### 使用 BackgroundWorker 类 `BackgroundWorker` 类是专门为在后台线程执行操作而设计的,它提供了更高级的功能,如进度报告和取消操作。示例代码如下: ```csharp using System; using System.ComponentModel; using System.Windows.Forms; namespace BackgroundWorkerExample { public partial class Form1 : Form { private BackgroundWorker backgroundWorker1; public Form1() { InitializeComponent(); backgroundWorker1 = new BackgroundWorker(); backgroundWorker1.DoWork += backgroundWorker1_DoWork; backgroundWorker1.RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted; } private void button1_Click(object sender, EventArgs e) { if (!backgroundWorker1.IsBusy) { backgroundWorker1.RunWorkerAsync(); } } private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { // 这里是后台线程执行的操作 } private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { // 后台操作完成后的处理 } } } ``` ### 使用 Task 类和 async/await 关键字 `Task` 类和 `async/await` 关键字是 .NET 4.0 引入的异步编程模型,它简化了异步编程的复杂性。示例代码如下: ```csharp using System; using System.Threading.Tasks; using System.Windows.Forms; namespace TaskExample { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private async void button1_Click(object sender, EventArgs e) { await Task.Run(() => { // 这里是异步执行的操作 }); // 异步操作完成后的处理 } } } ``` ### 跨线程调用问题及解决方案 在 C# 中,UI 控件不是线程安全的,只能在创建它们的线程(即 UI 线程)上进行访问和修改。若在非 UI 线程中对 UI 控件进行操作,会导致跨线程调用报错。可通过 `this.Invoke(委托实例)` 的方式将委托传递给 UI 线程的消息队列,UI 线程会在合适的时机从消息队列中取出该委托并执行 [^3]。示例代码如下: ```csharp private void UpdateUI(string text) { if (this.InvokeRequired) { this.Invoke(new Action<string>(UpdateUI), text); } else { label1.Text = text; } } ``` ### 最佳实践 - **避免阻塞 UI 线程**:将耗时操作放在后台线程执行,以保证 UI 的响应性。 - **线程安全**:确保在多线程环境下对共享资源的访问是线程安全的。 - **资源管理**:及时释放不再使用的线程资源,避免资源泄漏。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值