invoke委托解决“线程间操作无效: 从不是创建控件的线程访问它”的问题

来源:互联网 发布:紫胤真人知乎 编辑:程序博客网 时间:2024/05/14 19:14

1.问题描述

在线程中更改控件属性时,编译器报错:“线程间操作无效: 从不是创建控件的线程访问它”。查看资料后得知这个问题的根本在于,创建新线程后,新线程跟主线程同步运行,这时如果新线程提出对控件进行修改,将被编译器报错,因为可能会出现主线程跟新线程同时对同一控件进行修改的情况,这样就造成了程序执行的混乱。C#委托可以合理解决这一问题。取用委托后,被委托代码的执行顺序将被改变以避免冲突,具体原理请自行查阅。C#提供两种委托方法,一个是invoke,另一个是BeginInvoke。关于这两个的区别请自行查阅,在此只介绍BeginInvoke方法。

2.解决方法

1.使用delegate声明委托
public delegate void MyInvoke(byte[] receiveBytes);'
MyInvoke为委托名,()里面为要传给代理函数的值。
2.新写一个代理函数,来调用执行对控件的操作。
在线程里面加入委托来调用代理函数刷新控件。

3.实例

因为此项目较大,所以只摘出能表示出此问题的部分。在此,将删减无关代码以增加代码简洁度,所以不保证贴出阉割版代码能够运行。

未加委托之前的错误代码:

    public mainForm()    {        //        // Windows 窗体设计器支持所必需的        //        InitializeComponent();    //        // TODO: 在 InitializeComponent 调用后添加任何构造函数代码        //    }    /// <summary>    /// 清理所有正在使用的资源。    /// </summary>    /// <summary>    /// 应用程序的主入口点。    /// </summary>    [STAThread]    static void Main()     {        Application.Run(new mainForm());    }    Boolean flagLed1 = false;    Boolean flagLed2 = false;    Boolean flagTiltA = false;    Boolean flagTiltB = false;    Boolean flagManipA = false;    Boolean flagManipB = false;    Boolean flagThruster = false;    private void mainForm_Load(object sender, EventArgs e)    {    }    UdpClient udpClient = null;    Thread thread;           private void BUTTudpConnect_Click(object sender, EventArgs e)    {        if (BUTTudpConnect.Text == "connect")        {            udpClient = new UdpClient(Convert.ToInt32(TBLocalPort.Text));            try            {                udpClient.Connect(TBRemoteIP.Text, Convert.ToInt32(TBRemotePort.Text));                UdpSend( udpClient);                thread = new Thread(UdpListing); //这里新建了线程去持续监听UDP端口。                thread.Start(udpClient);                  thread.IsBackground = true;              }            catch (Exception ex)            {                MessageBox.Show(ex.ToString());            }            BUTTudpConnect.Text = "disconnect";        }        else        {            thread.Abort();            BUTTudpConnect.Text = "connect";            udpClient.Close();        }    }    private void UdpListing(Object obj)    {        UdpClient udpClient;        udpClient = (UdpClient)obj;        //IPEndPoint object will allow us to read datagrams sent from any source.        IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);        while(true)        {        Byte[] receiveBytes = udpClient.Receive(ref RemoteIpEndPoint);        string returnData = Encoding.ASCII.GetString(receiveBytes);        labelDHeading.Text = returnData.ToString(); //错误,在线程里更改了主线程窗体里的控件属性。        }    }

修改了三处地方,并在修改的地方标注了序号。如下
修改后的代码:

    public mainForm()    {        //        // Windows 窗体设计器支持所必需的        //        InitializeComponent();    //        // TODO: 在 InitializeComponent 调用后添加任何构造函数代码        //    }    /// <summary>    /// 清理所有正在使用的资源。    /// </summary>    /// <summary>    /// 应用程序的主入口点。    /// </summary>    [STAThread]    static void Main()     {        Application.Run(new mainForm());    }    Boolean flagLed1 = false;    Boolean flagLed2 = false;    Boolean flagTiltA = false;    Boolean flagTiltB = false;    Boolean flagManipA = false;    Boolean flagManipB = false;    Boolean flagThruster = false;    //更改处1/3 声明委托    public delegate void MyInvoke(byte[] receiveBytes);    private void mainForm_Load(object sender, EventArgs e)    {    }    UdpClient udpClient = null;    Thread thread;           private void BUTTudpConnect_Click(object sender, EventArgs e)    {        if (BUTTudpConnect.Text == "connect")        {            udpClient = new UdpClient(Convert.ToInt32(TBLocalPort.Text));            try            {                udpClient.Connect(TBRemoteIP.Text, Convert.ToInt32(TBRemotePort.Text));                UdpSend( udpClient);                thread = new Thread(UdpListing);                thread.Start(udpClient);   //这里新建了线程去持续监听UDP端口。                thread.IsBackground = true;              }            catch (Exception ex)            {                MessageBox.Show(ex.ToString());            }            BUTTudpConnect.Text = "disconnect";        }        else        {            thread.Abort();            BUTTudpConnect.Text = "connect";            udpClient.Close();        }    }    //更改出2/3 将更改控件的代码更改为代理函数以方便委托。    private void UpdateControls(byte[] receiveBytes)    {        string returnData = Encoding.ASCII.GetString(receiveBytes);        labelDHeading.Text = returnData.ToString();    }    private void UdpListing(Object obj)    {        UdpClient udpClient;        udpClient = (UdpClient)obj;        //IPEndPoint object will allow us to read datagrams sent from any source.        IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);        while(true)        {        Byte[] receiveBytes = udpClient.Receive(ref RemoteIpEndPoint);        MyInvoke mi = new MyInvoke(UpdateControls); //更改出3/3 委托        this.BeginInvoke(mi, new Object[] { receiveBytes });        }    }
阅读全文
0 0
原创粉丝点击