从运行循环UDP的单独线程更新多文本框在C#中侦听

时间:2013-07-15 06:48:50

标签: c# asynchronous udp

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

我得到了一个表单中的代码,它基本上在表单加载时添加了一个Task,以便运行UDP侦听器(在无限循环上)。每当Listener从UDP套接字获取内容时,我都会在multiline-textbox(this.textBox4.Text)中添加一行和消息。

但是我得到一个异常,说“跨线程操作无效:”从其创建的线程以外的线程访问Contol'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());
         }

    }
    ....

3 个答案:

答案 0 :(得分:1)

UI控件只能由创建它们的线程更改。您需要检查InvokeRequired(WinForms)或Dispatcher.CheckAccess()(WPF),然后调用Invoke / {{1 }}

答案 1 :(得分:0)

通常在使用c#并使用多线程时,您应该使用委托来使事情有效,而不是违反跨线程政治。 换句话说,不允许使用来自其他线程的一个线程中定义的对象。要实现这一点,您应该使用委托来强制拥有线程来执行调用线程的任务。

而不是:

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

你可以用这个: 定义这个方法:

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;     
}

并像这样从胎面上打电话给他们

SetText("a\r\nb");

答案 2 :(得分:0)

您可以尝试更改异步功能以使用当前的同步上下文

 return Task.Factory.StartNew<int>(() =>
    {
        if (WhatToDo == "OpenYourEars")
            goListening();
        return 1;
    },
    new CancellationToken(),
    TaskCreationOptions.None, 
    TaskScheduler.FromCurrentSynchronizationContext());