【问题标题】:What is the proper method for using invoke to avoid threading errors?使用调用避免线程错误的正确方法是什么?
【发布时间】:2014-07-03 23:40:20
【问题描述】:

我最近一直在学习 C#,但遇到了一个我似乎无法解决的问题。请原谅我,因为我对 C# 非常陌生,但我的问题是关于委托和调用的。 我已经在线阅读了很多教程,也观看了很多关于此的视频教程,但我的代码中仍然出现同样的错误,我似乎没有掌握其中的微妙之处。据我了解,委托是指向函数的指针,可用于从另一个线程调用该函数以更新文本框。我了解创建委托,我认为我做的很多,但是当我从威胁中调用委托时,我总是收到错误 Cross-thread operation not valid: Control 'textBox1' access from a thread than the thread它创建于。

回调似乎是功能性的,因为它正在调用它设计的函数,但是它似乎没有在正确的线程上执行它,我认为这是这样做的全部意义。我知道可以选择将警告中断设置为 false,但我宁愿了解我做错了什么以及如何正确编码这种类型的方法。感谢您提供的任何帮助、建议或答案,因为我不确定现在是否有任何教程能让我更接近了解我哪里出错了。

我的代码非常基础,因为我只是想了解正确编码多线程的最基本概念。下面是我的代码。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Media;
using System.Threading;
using System.Reflection;

namespace WindowsFormsApplication3
{   

    //declair delegate name(vars) CORRECT
    public delegate void Textboxdelegate(Int64 MyVar);

    public partial class Form1 : Form
    {

         public Form1()
         {
             InitializeComponent();
         }

         public void button1_Click(object sender, EventArgs e)
         {
             runme();
         }

         public void runme()
         {
             textBox1.Text = "87";
             textupdate(44);

             int target = 0;
             Textboxdelegate TD = new Textboxdelegate(textupdate);
             Number number = new Number(target, TD);
             TD(11);
             Thread thread1 = new Thread(new ThreadStart(number.worker));
             thread1.Start();

         }
        public void textupdate(Int64 cntr) 
        {
           textBox1.Text += cntr.ToString();
        }

    }

    class Number
    {

    int _target;
    Textboxdelegate _callbackMethod;

    public Number(int target, Textboxdelegate TDD)
    {
        this._target = target;
        this._callbackMethod = TDD;
    }

    public void worker()
    {

            Int64 counter = 0;
            byte[] lifeforms = new byte[2146435071];

            for (long y = 1; y <= 2146; y++)
            {

                for (long X = 1; X <= 1000000; X++)
                {
                    lifeforms[X * y] = 20;
                }

                counter += 1;
                if(_callbackMethod != null)
                {
                _callbackMethod(counter);
                }

            }

            MessageBox.Show("Done!");
        }

    }
}

【问题讨论】:

  • 欢迎来到 StackOverflow。请在发布您自己的问题之前尝试搜索。搜索错误消息是一个好的开始,它会引导您,例如,这里:stackoverflow.com/questions/142003/…
  • 跨线程操作发生在您尝试从其他线程调用线程时。即在 Windows 窗体中有一个 UI 线程[STA](单线程单元),现在如果您在此线程中创建了一些东西并尝试从其他线程访问它。你肯定会得到这个错误。反过来你使用 Invoke 方法将你的请求(说你想修改数据)放入 STA 线程的队列中,这将由相应的所有者线程和你处理不要从其他线程更新值。
  • 谢谢你。我当然会仔细阅读,但老实说,在过去的三天里,我每天阅读这样的帖子大约 8 小时。我理解起来可能有点慢(年纪大了)。了解我用我一直在努力理解的概念编写的代码中哪里出了问题,这对我有很大帮助。我想我已经很接近了,如果我能让这段代码正常工作,我或许可以很好地理解它,以适应变化和其他类型的类似概念。

标签: c# multithreading delegates invoke


【解决方案1】:

这个粗略的例子应该可以满足您的需求:

    //public delegate void Textboxdelegate(Int64 MyVar);

    public partial class Form1 : Form
    {

        public Form1()
        {
            InitializeComponent();
        }

        public Action<Int64> Textboxdelegate;
        public void runme()
        {
            textBox1.Text = "87";
            textupdate(44);

            int target = 0;
            Textboxdelegate = textupdate;
            Number number = new Number(target, Textboxdelegate,this);
            Textboxdelegate(11);
            Thread thread1 = new Thread(new ThreadStart(number.worker));
            thread1.Start();

        }
        public void textupdate(Int64 cntr)
        {
            textBox1.Text += cntr.ToString();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            runme();
        }

    }

    class Number
    {

        int _target;
        Action<Int64> _callbackMethod;
        Form1 frm;

        public Number(int target, Action<Int64> act,Form1 frm)
        {
            this._target = target;
            this._callbackMethod = act;
            this.frm = frm;
        }

        public void worker()
        {

            Int64 counter = 0;
            byte[] lifeforms = new byte[214643507];

            for (long y = 1; y <= 2146; y++)
            {

                for (long X = 1; X <= 100000; X++)
                {
                    lifeforms[X * y] = 20;
                }

                counter += 1;
                if (_callbackMethod != null)
                {
                    if (frm.InvokeRequired)
                    {
                        frm.Invoke(_callbackMethod,new object[]{counter});
                    }
                    else
                    {
                        _callbackMethod(counter);
                    }
                }

            }

            MessageBox.Show("Done!");
        }

    }

控件具有线程关联性,这意味着它们只能从创建它们的线程访问,而您可以通过另一个线程访问它们。现在 Control 类的 Invoke 方法(表单及其所有控件的基础)将让您可以安全地访问它们(非常概括的解释)。

【讨论】:

  • 非常感谢特里博齐奥。您的代码与我所做的方式不同,但很多都是相同的,非常有启发性。最棒的是 +2 赞!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-18
  • 2020-11-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多