【问题标题】:How to use Multithreading, locks, Socket Programming如何使用多线程、锁、套接字编程
【发布时间】:2010-06-29 13:17:02
【问题描述】:

在过去的 48 小时里,我一直在努力理解 MultithreadingSocket Programming。我尝试实现套接字编程并在不使用多线程时取得了成功。我对这两个主题都很陌生,并且在堆栈本身上提出了 2-3 个问题,需要同样的帮助。

谷歌搜索了很多后,我找到了一个解释Socket ProgrammingMultithreadingarticle,但我对这篇文章仍有很多疑问,并在Figure 5中卡住了文章。

private void AcceptConnections()
    {
        while (true)
        {
            // Accept a connection
            Socket socket = _serverSocket.Accept();
            ConnectionInfo connection = new ConnectionInfo();
            connection.Socket = socket;

            // Create the thread for the receives.
            connection.Thread = new Thread(ProcessConnection);
            connection.Thread.IsBackground = true;
            connection.Thread.Start(connection);

            // Store the socket
            lock (_connections) _connections.Add(connection);
        }
    }

在最后一行中,您可以看到 lock 已被占用,delegate ProcessConnection 上方的 3-4 行已绑定。

在这一点上,我不清楚这个锁是如何工作的。锁定后幕后发生了什么?为什么作者在这里使用了lock?如果没有锁会发生什么?线程 ProcessConnection 是如何工作的?什么事情同时发生?

我对所有这些问题感到困惑

我知道这里有一个问题列表,但如果您能帮助我理解使用多线程的方法,那将是一个很大的帮助。

【问题讨论】:

    标签: c# multithreading sockets locking


    【解决方案1】:

    connection.Thread.Start(connection) 调用ProcessConnection 启动一个新线程,将connection 作为state 参数传递。当前线程中的执行立即继续下一行,而ProcessConnection 在新线程中执行。

    ProcessConnectionConnectionInfo 传递给它的Socket 对象中获取AcceptConnections 对象并等待从套接字接收数据。当它接收到数据时,它会遍历connections 集合中的所有其他ConnectionInfo 对象,并将这些数据按顺序发送给每个对象。

    那么这里同时运行的是什么?好吧,我们让初始线程(称为线程 0)在无限循环中执行 AcceptConnections。然后对于我们接受的每个套接字连接,我们都有一个线程执行ProcessConnection

    之所以需要锁,是因为ProcessConnection 使用foreach 循环通过已知连接向它们发送数据。如果线程 0 在 foreach 中枚举集合时向集合添加新连接,则 InvalidOperationException 将被抛出 ProcessConnection

    lock 在这种情况下确实可以防止并发问题,但它也会导致潜在的性能问题。它不仅阻止AcceptConnectionsProcessConnection 枚举它时修改集合。它还可以防止任何两个执行ProcessConnection 的线程同时枚举集合。在这种情况下,更好的选择是ReaderWriterLockSlim,它允许多个线程同时读取集合。

    【讨论】:

    • lock (_connections) { foreach (ConnectionInfo conn in _connections) { if (conn != connection) { conn.Socket.Send( buffer, bytesRead, SocketFlags.None); } } } 这已被用于向每个套接字广播相同的消息?如您的第二段所述
    • 很好的解释,把所有的点都说清楚了。仍在阅读您的答案,并将文章作为参考。当我完整阅读它时会回复你。
    • @Shantanu:是的,foreach 循环是将接收到的消息广播到其他每个套接字的循环。
    • 只需阅读整篇文章并实现此处和文章中讨论的所有部分。您的解释与其他帮助一起提供了很大的帮助。我还要感谢 deltreme 的 gud 解释。
    【解决方案2】:

    我假设_connectionsList<ConnectionInfo>:列表不是线程安全的,并且该线程将项目添加到该列表中。如果另一个线程同时删除一个项目,结果将是不可预测的。所以你必须确保没有其他进程可以使用锁访问它。

    connection.Thread.Start(connection); 启动一个新线程,该线程将立即或很快启动。当前线程(您在此处看到的代码)将无法对其进行任何控制。这个新线程提供了一个ConnectionInfo 对象,所以它会知道在哪个套接字上执行任务。在当前线程不断监听新客户端的同时,ProcessConnection 函数将处理最近接受的客户端。

    【讨论】:

    • 很好地解释并感谢您宝贵的时间。我在这里有个问题。如何知道哪个对象是线程安全的,哪个不是。如果我锁定 _connection 以删除项目会发生什么。那么我可以同时将另一个对象添加到同一个列表中吗?
    • 在 MSDN 上,他们通常会告诉您对象是否是线程安全的。如果他们不说,你应该假设它不是。在添加和从列表中删除项目时都应该使用锁,否则可能会得到意外结果。 AddRemove 方法在底层执行了几行代码,当你同时调用它们时,它们可能会互相咬对方。给它加个锁会使一个线程等待另一个线程释放它的锁,所以它们不会互相干扰。
    【解决方案3】:

    在 C# 中,我认为通常在 CLR 中,每个对象都可能有一个 monitor 与之关联。这里_connections 是一个集合,它可能与从这个函数启动的线程共享(它们完成后可能会从集合中删除连接)。默认情况下,C# 中的集合不同步,您必须显式执行此操作,因此 lock(_connections) 语句可以防止集合上的 races

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-12-08
      • 2019-11-19
      • 2011-03-18
      • 2017-09-22
      • 1970-01-01
      • 2015-01-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多