枚举时向哈希表添加新值时出现问题

时间:2010-04-06 05:57:43

标签: c#-2.0 hashtable

`喜

我正在做一个简单的同步套接字编程,其中我使用了twothreads 一个用于接受客户端并将套接字对象放入集合中,其他线程将 循环遍历集合并通过套接字对象向每个客户端发送消息。

问题是

1.i连接到客户端到服务器并开始发送消息

2.现在我想连接一个新客户端,而这样做我无法更新集合并添加 我的hashtable.it的一个新客户端引发了一个异常“集合修改.Enumeration操作可能无法执行”

如何在哈希表中添加新值而不会出现问题。

 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 个答案:

答案 0 :(得分:2)

是。不要那样做。

这里更大的问题是不安全的多线程。 最基本的“答案”就是说:在共享对象上使用同步锁。然而,这隐藏了许多重要方面(比如理解正在发生的事情)并且在我的脑海中并不是解决这个问题的真正解决方案。

答案 1 :(得分:2)

您无法在迭代它时修改HashtableDictionaryList或类似内容 - 无论是在同一个线程中还是在不同的线程中。 .NET 4中有并发集合允许这样做,但我假设你没有使用.NET 4.(出于兴趣,为什么你仍然使用Hashtable而不是通用Dictionary ?)

也不应该在一个线程中修改Hashtable,而在没有任何同步的情况下从另一个线程读取它时。

解决此问题的最简单方法是:

  • 创建一个用于锁定的新的只读变量
  • 在添加到Hashtable之前获取锁:

    lock (tableLock)
    {
        ClientTable.Add(ReceiveSock.RemoteEndPoint.ToString(), ReceiveSock);
    }
    
  • 如果要进行迭代,请在锁定内的Hashtable中创建数据的新副本

  • 迭代副本而不是原始表

你肯定在这里需要 Hashtable吗?它看起来像一个简单的List<T>ArrayList是可以的,其中每个条目是套接字或可能是包含套接字的自定义类型以及您需要的任何其他信息。您似乎没有在表上进行任意查找。