如何打断其他std::threads c++
How to interrupt other std::threads c++
我有一个以每个客户端线程方式构建的服务器。最近,我遇到了一个问题,我真的很难想出解决办法,所以我想寻求帮助。
我的服务器有一个大厅,大厅里有很多房间(都是用户的),房间里有玩家。每个房间都有一个管理员,当管理员选择离开时 - 房间关闭,所有用户都应该 return 到大厅。
现在,我已经有了一个工作代码 - 但问题是,我不知道我应该如何让其他客户也退出房间。线程中的代码运行如下:
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
}
将布尔值从一个客户端的线程弄乱到另一个客户端没有问题(因为它们不完全是局部变量,它们是我可以通过处理管理员选择关闭线程的线程更改的几个条件房间)。
问题发生在条件确实改变时,而 while 循环应该在下一次迭代之前退出。为什么会发生?因为在迭代开始时有一个 recv()
调用,等待客户端的消息。
现在,条件好了,一切都好了,但是循环不会继续(所以它不会到达下一次迭代 - 看到条件为假)直到服务器收到来自客户端(它不应该,因为关闭房间不依赖于普通用户 - 用户只接收和警告,这也是通过管理员的线程发送的,关于房间被关闭)。
因此我的问题是:
我怎样才能做我想做的事?我怎样才能让这些用户跳出循环,return 进入大厅(与管理员一起这样做没有问题,因为他的线程是做所有事情的线程,他 return s 到大厅成功)而不改变整个架构从一个线程每个客户端的方式?
除了 πìντα ῥεῖ 的评论,您可以尝试实施 Provider-Consumer pattern.
使用一个队列来存储客户端消息并使用该循环从中读取,如果没有要读取的消息就继续。这样循环就不会等待消息到达,即 "Provider's" 作业(循环是消费者,因为正在使用队列中的消息)。
因此,将调用 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.
}
}
"Because in the beginning of the iteration there's a recv() call, which awaits the client's messages."
可能您不希望此时在第一位进行阻塞 recv()
调用。
Windows Socket API 中有 select()
函数,可以用来观察多个套接字文件描述符的状态变化。
基本上你会有一个线程 运行 一个循环,并轮询 select()
调用的 return 值。不要用紧密的循环占用 CPU,应该指定一个合理的超时值,但零超时也是可能的,并且只轮询可用状态。
如果 return 值表明观察到的套接字之一 (select() > 0
) 有一些可用的操作,您可以检查观察到的套接字文件描述符,如果它们是使用
FD_ISSET(s, *set)
宏。
Because in the beginning of the iteration there's a recv() call, which awaits the client's messages.
我不熟悉您使用的具体细节,但可以在类似情况下使用的两个技巧是:
- 让您的服务器将字节放入
recv()
从中读取的流中。
- 关闭流。
意图是这些中的任何一个都会迫使 recv()
到 return。
如果你不能用 winsock 做到这一点,那么另一种解决方案是添加另一层。您编写了一个包装器 class 来管理如何从套接字读取的复杂性,同时仍然能够从其他来源接收通知。那么您的客户只使用包装器 class 来接收通信。
从长远来看,这个解决方案可能比直接使用 recv()
更好,因为它分离了关注点;客户端代码只关心如何与客户端打交道,而不关心如何进行稳健通信的细节,通信代码只需要处理如何接收和中继通信。
重构您的代码,使您在 recv
上只有一个地方被阻塞。然后,您可以随心所欲地移动客户端,而不必中断 recv
中阻塞的线程。如果客户端发送一条消息,您仍然希望收到一条消息,对吗?
因此,当房间关闭时,关闭房间的线程可以将客户移出房间,而无需打扰等待来自这些客户的消息的线程。
我有一个以每个客户端线程方式构建的服务器。最近,我遇到了一个问题,我真的很难想出解决办法,所以我想寻求帮助。
我的服务器有一个大厅,大厅里有很多房间(都是用户的),房间里有玩家。每个房间都有一个管理员,当管理员选择离开时 - 房间关闭,所有用户都应该 return 到大厅。
现在,我已经有了一个工作代码 - 但问题是,我不知道我应该如何让其他客户也退出房间。线程中的代码运行如下:
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
}
将布尔值从一个客户端的线程弄乱到另一个客户端没有问题(因为它们不完全是局部变量,它们是我可以通过处理管理员选择关闭线程的线程更改的几个条件房间)。
问题发生在条件确实改变时,而 while 循环应该在下一次迭代之前退出。为什么会发生?因为在迭代开始时有一个 recv()
调用,等待客户端的消息。
现在,条件好了,一切都好了,但是循环不会继续(所以它不会到达下一次迭代 - 看到条件为假)直到服务器收到来自客户端(它不应该,因为关闭房间不依赖于普通用户 - 用户只接收和警告,这也是通过管理员的线程发送的,关于房间被关闭)。
因此我的问题是:
我怎样才能做我想做的事?我怎样才能让这些用户跳出循环,return 进入大厅(与管理员一起这样做没有问题,因为他的线程是做所有事情的线程,他 return s 到大厅成功)而不改变整个架构从一个线程每个客户端的方式?
除了 πìντα ῥεῖ 的评论,您可以尝试实施 Provider-Consumer pattern.
使用一个队列来存储客户端消息并使用该循环从中读取,如果没有要读取的消息就继续。这样循环就不会等待消息到达,即 "Provider's" 作业(循环是消费者,因为正在使用队列中的消息)。
因此,将调用 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.
}
}
"Because in the beginning of the iteration there's a recv() call, which awaits the client's messages."
可能您不希望此时在第一位进行阻塞 recv()
调用。
Windows Socket API 中有 select()
函数,可以用来观察多个套接字文件描述符的状态变化。
基本上你会有一个线程 运行 一个循环,并轮询 select()
调用的 return 值。不要用紧密的循环占用 CPU,应该指定一个合理的超时值,但零超时也是可能的,并且只轮询可用状态。
如果 return 值表明观察到的套接字之一 (select() > 0
) 有一些可用的操作,您可以检查观察到的套接字文件描述符,如果它们是使用
FD_ISSET(s, *set)
宏。
Because in the beginning of the iteration there's a recv() call, which awaits the client's messages.
我不熟悉您使用的具体细节,但可以在类似情况下使用的两个技巧是:
- 让您的服务器将字节放入
recv()
从中读取的流中。 - 关闭流。
意图是这些中的任何一个都会迫使 recv()
到 return。
如果你不能用 winsock 做到这一点,那么另一种解决方案是添加另一层。您编写了一个包装器 class 来管理如何从套接字读取的复杂性,同时仍然能够从其他来源接收通知。那么您的客户只使用包装器 class 来接收通信。
从长远来看,这个解决方案可能比直接使用 recv()
更好,因为它分离了关注点;客户端代码只关心如何与客户端打交道,而不关心如何进行稳健通信的细节,通信代码只需要处理如何接收和中继通信。
重构您的代码,使您在 recv
上只有一个地方被阻塞。然后,您可以随心所欲地移动客户端,而不必中断 recv
中阻塞的线程。如果客户端发送一条消息,您仍然希望收到一条消息,对吗?
因此,当房间关闭时,关闭房间的线程可以将客户移出房间,而无需打扰等待来自这些客户的消息的线程。