0

我正在使用 C# .net 4.0 VS 2010。

我得到了一个表单代码,它基本上在表单加载时添加了一个任务,以便运行 UDP 侦听器(无限循环)。每当侦听器从 UDP 套接字获取某些内容时,我都会将一行和消息添加到多行文本框(this.textBox4.Text)。

但是我得到一个异常说“跨线程操作无效:“控制'textBox4'是从创建它的线程以外的线程访问的。”

我不想仅仅为了传递值而结束循环。有没有办法做到这一点?这是我的代码:

    //main form load menu
    private void frm_Menu_Load(object sender, EventArgs e)
    {
        Task<int> Listening = DoWorkAsync(1, "OpenYourEars");
        .... // more code here                      
    }

    //async function
    public Task<int> DoWorkAsync(int milliseconds, string WhatToDo)
    {
        return Task.Factory.StartNew<int>(() =>
        {
            if (WhatToDo == "OpenYourEars")
                goListening();
            ... // more codes here
            return 1;
        });
    }

   //Listening on UDP socket
    public void goListening()
    {
        bool done = false;
        UdpClient listener = new UdpClient(listenPort);
        IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, listenPort);
        string received_data;
        byte[] receive_byte_array;
        try
        {
            while (!done)
            {
                receive_byte_array = listener.Receive(ref groupEP);
                received_data = Encoding.ASCII.GetString(receive_byte_array, 0, receive_byte_array.Length);

                // display on TextBox4
                this.textBox4.Text = "a\r\nb";
                this.textBox4.Text = received_data.ToString().Trim();
            }
        }
        catch (Exception e)
        {
            //gives "Contol 'textBox4' accessed from a thread other than 
            //the thread it was created on." when receiving a message.
            MessageBox.Show(e.ToString());
        }
        listener.Close();
    }

第 2 版 - 在 @cremor 和 @George87 的回答之后

    private void frm_Menu_Load(object sender, EventArgs e)
    {
        MyValue = "Menu,7";
        Task<int> Listening = DoWorkAsync(1, "OpenYourEars");
        .... // more code here                      
    }

    private Task<int> DoWorkAsync(int milliseconds, string WhatToDo)
    {
        return Task.Factory.StartNew<int>(() =>
        {
            if (WhatToDo == "OpenYourEars")
                goListening();
            .... // more codes here
            return 1;
        });
    }

    //Listening
    private void goListening()
    {
        bool done = false;
        UdpClient listener = new UdpClient(listenPort);
        IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, listenPort);
        string received_data;
        byte[] receive_byte_array;
        try
        {
            while (!done)
            {
                receive_byte_array = listener.Receive(ref groupEP);
                received_data = Encoding.ASCII.GetString(receive_byte_array, 0, receive_byte_array.Length);

                string aa = received_data.ToString().Trim();
                if ( aa != "")
                {
                    SetText("a\r\nb");
                    SetText(received_data.ToString().Trim());
                    aa = "";
                }
            }
        }
        catch (Exception e)
        {
            MessageBox.Show(e.ToString());               
        }
        listener.Close();
    }

    private delegate void SetTextCallback(string text);
    private void SetText(string text)
    {
        try
        {
              if (this.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(SetText);
                this.BeginInvoke(d, new object[] { text });
            }
            else
            {
                SetText(text);
            }
            this.textBox4.Text = text;
        }
         catch (Exception e)
         {
             MessageBox.Show(e.ToString());
         }

    }
    ....
4

3 回答 3

1

UI controls can only be changed by the thread they were created in. You need to check InvokeRequired (WinForms) or Dispatcher.CheckAccess() (WPF) and then call Invoke/BeginInvoke.

于 2013-07-15T07:17:17.463 回答
0

Normally when using c# and use multi threading you should use delegates to make things work and not violate Cross-thread politics. In other words you are not allowed to use objects defined in one thread from other threads. To make this happen you should use delegates to force the owning thread to perform the task for the calling thread.

Instead of:

  // display on TextBox4
  this.textBox4.Text = "a\r\nb";

you could use this: define this methods:

delegate void SetTextCallback(string text);
private void SetText(string text)
{
   if (this.InvokeRequired)
   {
      SetTextCallback d = new SetTextCallback(SetText);
      this.Invoke(d, new object[] { text });
   }
   else
   {
      SetText(text);
   }
   this.textBox1.Text = text;     
}

and call them from the tread like this

SetText("a\r\nb");
于 2013-07-15T06:53:50.177 回答
0

您可以尝试更改异步函数以使用当前同步上下文

 return Task.Factory.StartNew<int>(() =>
    {
        if (WhatToDo == "OpenYourEars")
            goListening();
        return 1;
    },
    new CancellationToken(),
    TaskCreationOptions.None, 
    TaskScheduler.FromCurrentSynchronizationContext());
于 2013-07-15T08:14:22.873 回答