Проблема при добавлении нового значения в хеш-таблицу при перечислении - PullRequest
1 голос
/ 06 апреля 2010

`привет

Я занимаюсь простым синхронным программированием сокетов, в котором я использовал два потока один для принятия клиента и помещения объекта сокета в коллекцию, другой поток перебрать коллекцию и отправить сообщение каждому клиенту через объект сокета.

проблема

1.i подключаюсь к клиентам на сервере и начинаю отправлять сообщения

2. Теперь я хочу подключить нового клиента, при этом я не могу обновить коллекцию и добавить новый клиент для моего hashtable.it вызывает исключение «коллекция изменена. Операция перечисления может не выполняться»

как добавить значение NEW без проблем в хеш-таблице.

 private void Listen()
    {
        try
        {
            //lblStatus.Text = "Server Started Listening";
            while (true)
            {
                    Socket ReceiveSock = ServerSock.Accept();
                    //keys.Clear();
                    ConnectedClients = new ListViewItem();
                    ConnectedClients.Text = ReceiveSock.RemoteEndPoint.ToString();
                    ConnectedClients.SubItems.Add("Connected");
                    ConnectedList.Items.Add(ConnectedClients);
                    ClientTable.Add(ReceiveSock.RemoteEndPoint.ToString(), ReceiveSock);
                    //foreach (System.Collections.DictionaryEntry de in ClientTable)
                    //{

                    //    keys.Add(de.Key.ToString());
                    //}
                    //ClientTab.Add(
                    //keys.Add(

            }
            //lblStatus.Text = "Client Connected Successfully.";
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }

    private void btn_receive_Click(object sender, EventArgs e)
    {
        Thread receiveThread = new Thread(new ThreadStart(Receive));
        receiveThread.IsBackground = true;
        receiveThread.Start();
    }
    private void Receive()
    {
        while (true)
        {
            //lblMsg.Text = "";
            byte[] Byt = new byte[2048];
            //ReceiveSock.Receive(Byt);
            lblMsg.Text = Encoding.ASCII.GetString(Byt);
        }
    }

    private void btn_Send_Click(object sender, EventArgs e)
    {
        Thread SendThread = new Thread(new ThreadStart(SendMsg));
        SendThread.IsBackground = true;
        SendThread.Start();
    }

    private void btnlist_Click(object sender, EventArgs e)
    {
        //Thread ListThread = new Thread(new ThreadStart(Configure));
        //ListThread.IsBackground = true;
        //ListThread.Start();
    }
    private void SendMsg()
    {
        while (true)
        {
            try
            {
                foreach (object SockObj in ClientTable.Keys)
                {
                    byte[] Tosend = new byte[2048];
                    Socket s = (Socket)ClientTable[SockObj];
                    Tosend = Encoding.ASCII.GetBytes("FirstValue&" + GenerateRandom.Next(6, 10).ToString());
                    s.Send(Tosend);
                    //ReceiveSock.Send(Tosend);
                    Thread.Sleep(300);
                }

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

    }

Ответы [ 2 ]

2 голосов
/ 06 апреля 2010

Вы просто не можете изменить Hashtable, Dictionary, List или что-то подобное, пока вы итерируете по нему - в том же потоке или в другом.В .NET 4 есть параллельные коллекции, которые позволяют это, но я предполагаю, что вы не используете .NET 4. (Из интереса, почему вы все еще используете Hashtable, а не универсальный Dictionary?)

Вы также не должны изменять Hashtable из одного потока при чтении из него в другом потоке без какой-либо синхронизации.

Самый простой способ исправить это:

  • Создать новую переменную только для чтения, используемую для блокировки
  • Получить блокировку перед добавлением в Hashtable:

    lock (tableLock)
    {
        ClientTable.Add(ReceiveSock.RemoteEndPoint.ToString(), ReceiveSock);
    }
    
  • Если вы хотите выполнить итерацию, создайте новую копию данных в Hashtable в пределах блокировки

  • Выполните итерацию по копии вместо исходной таблицы

Вам определенно даже нужно a Hashtable здесь?Мне кажется, что простой List<T> или ArrayList будет в порядке, где каждая запись является либо сокетом, либо, возможно, пользовательским типом, содержащим сокет и любую другую необходимую вам информацию.Похоже, вы не выполняете произвольный поиск на столе.

2 голосов
/ 06 апреля 2010

Да. Не делай этого.

Большая проблема здесь - небезопасная многопоточность. Самый простой «ответ» - просто сказать: используйте блокировку синхронизации для общего объекта. Однако это скрывает ряд важных аспектов (например, понимание того, что происходит) и не является реальным решением этой проблемы на мой взгляд.

...