• C#线程间操作无效:从不是创建控件“textbox1”的线程访问它


      在C#的多线程访问中,在线程间的相互访问时因为线程安全问题有访问限制,在创建一般线程时,对于界面元素访问时这样的问题比较常见。

      比如,创建一个form1,上面放置一个textbox控件,创建一个线程去访问textbox,界面如下:

      按钮buuton1的代码:

    1. private void button1_Click(object sender, EventArgs e)
    2. {
    3. var thread1 = new System.Threading.Thread(Func1);
    4. thread1.Start();
    5. }

      就是简单地创建一个线程,线程里面运行的func1代码:

    1. private void Func1()
    2. {
    3. for(int i = 0; i < 5; i++)
    4. {
    5. textBox1.Text = textBox1.Text+ $"线程{Thread.CurrentThread.ManagedThreadId}执行{i}."+Environment.NewLine;
    6. }
    7. }

      运行,点击button1按钮系统会报错:

      意思是说:线程间操作无效:从不是创建控件“textbox1”的线程访问它,这是不允许的。

      线程存在安全保护机制,并不能随意访问,因为这样存在冲突的可能。

      解决这个问题,最直接的方法是在界面初始化后去掉控件的跨线程非法访问属性,即将Control.CheckForIllegalCrossThreadCalls属性设置为false即可。

    1. public Form1()
    2. {
    3. InitializeComponent();
    4. Control.CheckForIllegalCrossThreadCalls = false;
    5. }

      这样结果就出来了。

      一般情况下,还是不要设置Control.CheckForIllegalCrossThreadCalls的属性,那么怎样可以达到修改textbox1的值呢?

      可以通过委托来解决。

    1. public partial class Form1 : Form
    2. {
    3. public Form1()
    4. {
    5. InitializeComponent();
    6. }
    7. private void button1_Click(object sender, EventArgs e)
    8. {
    9. var thread1 = new System.Threading.Thread(Func1);
    10. thread1.Start();
    11. }
    12. public void SetText(string SText)
    13. {
    14. textBox1.Text = textBox1.Text + SText + Environment.NewLine;
    15. }
    16. private void Func1()
    17. {
    18. string Str = "";
    19. for (int i = 0; i < 5; i++)
    20. {
    21. Str= $"线程{Thread.CurrentThread.ManagedThreadId}执行{i}."+Environment.NewLine;
    22. if (textBox1.InvokeRequired)
    23. {
    24. Action SetText111 = delegate { SetText(Str); };
    25. textBox1.Invoke(SetText111);
    26. }
    27. else
    28. {
    29. textBox1.Text = Str;
    30. }
    31. }
    32. }
    33. }

      这样也可以达到目的,或者直接写更简单:

    1. public partial class Form1 : Form
    2. {
    3. public Form1()
    4. {
    5. InitializeComponent();
    6. }
    7. private void button1_Click(object sender, EventArgs e)
    8. {
    9. var thread1 = new System.Threading.Thread(Func2);
    10. thread1.Start();
    11. }
    12. private void Func2()
    13. {
    14. textBox1.Invoke(() =>
    15. {
    16. for (int i = 0; i < 5; i++)
    17. {
    18. textBox1.Text = textBox1.Text+ $"线程{Thread.CurrentThread.ManagedThreadId}执行{i}." + Environment.NewLine;
    19. }
    20. });
    21. }
    22. }

      效果也是一样的。

      在C#中,需要注意Invoke和begininvoke的区别。

      control.invoke(参数delegate):在拥有此控件的基础窗口句柄的线程上执行指定的委托,注意是同步。
      control.begininvoke(参数delegate):在创建控件的基础句柄所在线程上异步执行指定委托,注意这里执行的是异步。

      在跨线程请求时,常检验textBox1.InvokeRequired属性,即是否跨线程请求。

  • 相关阅读:
    kotlin if when 循环
    算能RISC-V通用云编译飞桨paddlepaddle@openKylin留档
    [附源码]java毕业设计智能视频推荐网站
    C++与正则表达式
    把backtrader改造成金融强化学习回测引擎
    听潮汐,筑灯塔,聚千帆:智慧港口全球创新实验室启航时
    [2024年]-flink面试真题(三)
    jmeter请求接口问题小记
    互联网摸鱼日报(2022-11-02)
    互联网架构演进之路
  • 原文地址:https://blog.csdn.net/dawn0718/article/details/128069546