简体   繁体   中英

Safe Multi-thread access in C#

I am trying to implement a chat. I have taken a code here

When I send messages I have an error:

Control 'ListMessage' name accessed from a thread other than the thread it was created on.

where ListMessage is a ListView added with Visual Studio.

       private void buttonSend_Click(object sender, EventArgs e)
        {
            //convert string message to byte[]
            ASCIIEncoding aEncoding = new ASCIIEncoding();
            byte[] sendingMessage = new byte[1500];
            sendingMessage = aEncoding.GetBytes(textMessage.Text);
            //sending the encoded message
            sck.Send(sendingMessage);
            //adding to the list box
            listMessage.Items.Add("Me:" + textMessage.Text);
            textMessage.Text = "";
        }

        private void MessageCallBack(IAsyncResult aResult)
        {
            try
            {
                byte[] receivedData = new byte[1500];
                receivedData = (byte[])aResult.AsyncState;
                //converting byte[] to string
                ASCIIEncoding aEncoding = new ASCIIEncoding();
                string receivedMessage = aEncoding.GetString(receivedData);

                //Adding this message into list box
///// error is given in the following line/////
                listMessage.Items.Add("Friend:" + receivedMessage);

                buffer = new byte[1500];
                sck.BeginReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref epRemote, new AsyncCallback(MessageCallBack), buffer);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

I have tried to use Invoke as described here but I have errors

        private void MessageCallBack(IAsyncResult aResult)
        {
            try
            {
                byte[] receivedData = new byte[1500];
                receivedData = (byte[])aResult.AsyncState;
                //converting byte[] to string
                ASCIIEncoding aEncoding = new ASCIIEncoding();
                string receivedMessage = aEncoding.GetString(receivedData);

                //Adding this message into list box
                //listMessage.Items.Add("Friend:" + receivedMessage);
                listMessage.AddItemSafe("Friend:" + receivedMessage);

                buffer = new byte[1500];
                sck.BeginReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref epRemote, new AsyncCallback(MessageCallBack), buffer);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        private void AddItemSafe(string text)
        {
            if (this.InvokeRequired)
            {
                AddItemSafeCallback a = new AddItemSafeCallback(AddItemSafe);
                this.Invoke(a, new object[] { text });
            }
            else
            {
                this.Items.Add(text);
            }
        }

        delegate void AddItemSafeCallback(string text);

Erros are:

  • 'ListBox' does not contain a definition fo 'AddItemSafe' and no extension methode. 'AddItemSafe' accepting a first argument of type 'ListBox'could be found (are you missing a using directive or an assembly reference'
  • 'MyUserControl' does not contain a definition fo 'AddItemSafe' and no extension methode. 'Item' accepting a first argument of type 'ListBox'could be found (are you missing a using directive or an assembly reference'

My questions:

  • Do you believe the way I try to fix to initial problem is the good one ?
  • If yes, what is the problem with the solution I try to implement

Yes, the approach is quite correct, but you did miss a few things with the implementation.

The AddItemSafe method is just a method inside your form descendant class, not a method on listMessage (whatever type that may be). So when you call that, you only have to specify the method name and the parameter and not call it on an another object instance.

...
ASCIIEncoding aEncoding = new ASCIIEncoding();
string receivedMessage = aEncoding.GetString(receivedData);
//Adding this message into list box
//listMessage.Items.Add("Friend:" + receivedMessage);

// !!!!Note difference here:
AddItemSafe("Friend:" + receivedMessage);
...

Then, inside the AddItemSafe method, you do exactly what you would do in a single threaded environment, ie add to the items of listMessage . If invoke is required, then you wrap the whole thing into a delegate.

private void AddItemSafe(string text)
{
  if (this.InvokeRequired)
  {
    AddItemSafeCallback a = new AddItemSafeCallback(AddItemSafe);
    this.Invoke(a, new object[] { text });
  }
  else
  {
    // !!!!Note difference here:
    listmessage.Items.Add(text);
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM