• winform 跨线程访问问题


    一、问题描述

    进行winform 开发我们在进行数据交换时避免不了使用多线程或异步方法,这样操作也将避免不了跨线程对控件进行操作(赋值、修改属性)。

    下面通过一个测试说明一下问题

    点击一个按钮异步对textbox进行赋值

    运行测试结果

    1 private void button1_Click(object sender, EventArgs e)
    2 {        
    3               Action<string> action = new Action<string>((str) =>
    4               {
    5                   // 解决跨线程赋值
    6                   this.textBox1.Text = str;              
    7               });
    8               action.BeginInvoke("Test", null, null);      
    9 }
    View Code

    产生错误的原因:textBox1是由主线程创建的,异步方法(或子线程)是另外创建的一个线程,在.NET上执行的是托管代码,C#强制要求这些代码必须是线程安全的,即不允许跨线程访问Windows窗体的控件。

    二、解决方法

    1.在窗体的加载事件中,将C#内置控件(Control)类的CheckForIllegalCrossThreadCalls属性设置为false,屏蔽掉C#编译器对跨线程调用的检查。

    1 //取消跨线程的访问
    2 Control.CheckForIllegalCrossThreadCalls = false;
    View Code

    使用上述的方法虽然可以保证程序正常运行并实现应用的功能,但是在实际的软件开发中,做如此设置是不安全的(不符合.NET的安全规范),在产品软件的开发中,此类情况是不允许的。如果要在遵守.NET安全标准的前提下,实现从一个线程成功地访问另一个线程创建的空间,要使用C#的方法回调机制。

    2.使用回调函数 ( 委托的应用 )

    (1) 定义声明回调

    (2) 初始化回调

    (3) 触发动作

     1         /// <summary>
     2         /// 定义委托
     3         /// </summary>
     4         /// <param name="str"></param>
     5         private delegate void SetValueDelegate(string str);
     6 
     7         /// <summary>
     8         /// 声明委托
     9         /// </summary>
    10         SetValueDelegate test;
    11 
    12         /// <summary>
    13         /// 回调方法
    14         /// </summary>
    15         /// <param name="str"></param>
    16         private void SetTbValue(string str)
    17         {
    18             this.textBox1.Text = str;
    19         }
    20 
    21         private void button1_Click(object sender, EventArgs e)
    22         {
    23             // 实例化委托    
    24             test = new SetValueDelegate(SetTbValue);
    25 
    26             Action<string> action = new Action<string>((str) =>
    27             {
    28                   // 解决跨线程赋值                 
    29                   this.textBox1.Invoke(test, str);
    30              });
    31 
    32             action.BeginInvoke("Test", null, null);
    33         }
    View Code

    我个人比较使用喜欢使用事件解决(原理同委托一样),但省掉不少代码

     1         private void button1_Click(object sender, EventArgs e)
     2         {
     3             Action<string> action = new Action<string>((str) =>
     4               {
     5                   // 解决跨线程赋值            
     6                   this.textBox1.Invoke(
     7                       new Action<string>((param) =>
     8                       {
     9                           this.textBox1.Text = param;
    10                       }
    11                       ), str);
    12               });
    13 
    14             action.BeginInvoke("Test", null, null);
    15         }
    View Code
  • 相关阅读:
    将读取的.raw文件转换为tensor张量送入网络
    批量解压zip文件到指定位置
    三维医学图像深度学习,数据增强方法(monai):RandHistogramShiftD, Flipd, Rotate90d
    springboot的starter原理探究 + 如何自定义自己的starter
    构建一个可执行的 JAR 并运行
    git:当远程仓库迁移后,需要更改远程仓库指向
    正则表达式验证版本号
    HbuilderX 更改内置终端为 gitBash
    不用 Typescript 的理由
    用户协议和隐私政策的实现方式调查研究
  • 原文地址:https://www.cnblogs.com/wktang/p/10418561.html
Copyright © 2020-2023  润新知