如何中断其他std :: threads c ++

时间:2015-05-06 16:01:10

标签: c++ multithreading winsock2

我有一个以每个客户端线程方式构建的服务器。最近,我遇到了一个问题,我很难找到解决方案,所以我想找一些帮助。

我的服务器主办一个大厅,大堂有很多房间(所有房间都由用户拥有),房间里有玩家。每个房间都有一个管理员,当管理员选择离开时 - 房间关闭,所有用户都应该返回大厅。

现在,我已经有了一个正常工作的代码 - 但问题是,我不知道如何让其他客户也离开房间。线程中运行的代码如下:

while(in_lobby)
{
//Receive a message
//Do stuff
//In certain cases change the Boolean to fit to the situation
//Send a comeback
}

while(in_room)
{
//Receive a message
//Do stuff
//In certain cases change the Boolean to fit to the situation
//Send a comeback
}

while(in_game)
{
//When a game started
//Not practical right now, though
}

从一个客户端的线程到另一个客户端的线程没有问题(因为它们不是本地变量,它们是我可以通过处理管理员选择关闭房间的线程更改的几个条件)

当条件DOES改变时,问题发生,并且while循环应该在下一次迭代之前退出。它为什么会发生?因为在迭代开始时有recv()次呼叫,等待客户的消息。

现在,状况还可以,一切正常,但循环不会继续(所以它不会到达下一次迭代 - 看到条件为假),直到服务器收到某个来自客户的消息(它不应该,因为关闭房间并不依赖于普通用户 - 用户只接收并通过管理员的线程发送警报,关于房间被关闭)。

我的问题是:

我怎样才能表现自己想要的?如何让这些用户突破循环,返回大厅(管理员这样做是没有问题的,因为他的线程就是那个完成所有事情并且他成功返回大厅的线程)而不改变整体从每个客户端的线程架构?

4 个答案:

答案 0 :(得分:1)

  

“因为在迭代开始时有一个recv()调用,等待客户端的消息。”

可能你不想在第一名的时候拨打recv()电话 Windows Socket API中有select()函数,您可以使用它来观察许多套接字文件描述符的状态更改。

基本上,您将有一个运行循环的线程,并轮询select()调用的返回值。不要使用紧密循环来占用CPU,应该指定合理的超时值,但是可以使用零超时,并且仅轮询可用状态。

如果返回值表示某个观察到的套接字(select() > 0)有某些操作可用,则可以检查观察到的套接字文件描述符(如果它们是使用

触发的)
FD_ISSET(s, *set)

宏。

答案 1 :(得分:1)

  

因为在迭代开始时有一个recv()调用,等待客户端的消息。

如果你使用的是什么,我不熟悉这些细节,但是在类似情况下可以使用的两个技巧是:

  • 让您的服务器将字节放入recv()读取的流中。
  • 关闭流。

意图是其中任何一个都会强制recv()返回。

如果你不能用winsock做这个,那么另一个解决方案是添加另一个层。您编写了一个包装类,它管理从套接字读取的复杂性,同时仍然能够从其他来源接收通知。然后您的客户端只使用包装类来接收通信。

从长远来看,这个解决方案可能比直接使用recv()更好,因为它分散了关注点;客户端代码只关心如何处理客户端而不是如何进行强健通信的细节,通信代码只需要处理如何接收和中继通信。

答案 2 :(得分:0)

除了πάνταῥεῖ的评论,你可以尝试实现Provider-Consumer pattern

使用队列存储客户端消息,并使用该循环从中读取消息,如果没有要读取的消息继续。这样循环不会等待消息到达,即“提供者”作业(循环是消费者,因为它正在消耗来自队列的消息)。

因此,将调用recv的代码移出循环并使用它来提供队列。

伪代码:

Queue queue; // This has to be thread save.

class Provider: Thread
{
    void run(queue)
    {
      while(1)
      {
          message = recv();       // This is where waiting occurs.
          queue.push(message);
      }
    }   
}


// This looks like it fits inside another thread. ;)
while (some_condition)
{
    message = queue.pop(); // This returns immediately.
    if (message)
    {
      //... so some things.  
    }
}

答案 3 :(得分:0)

重构您的代码,以便在recv上阻止您只有一个地方。然后,您可以随心所欲地移动客户端,而不必破坏recv中阻止的线程。如果客户端发送一条消息,您仍然希望收到消息,对吗?

因此,当一个房间关闭时,关闭房间的线程可以将客户端移出它,而不需要打扰等待来自这些客户端的消息的线程。