Ale*_*lex 7 c++ multithreading
我一直遇到试图运行具有以下属性的线程的问题:
我见过的一个解决方案如下:
void class::run()
{
while(!exit_flag)
{
if (resource_ready)
use_resource();
}
}
Run Code Online (Sandbox Code Playgroud)
这满足第1,2和3点,但是忙碌的等待循环使用100%CPU.
一个潜在的解决方法是将睡眠声明放入:
void class::run()
{
while(!exit_flag)
{
if (resource_ready)
use_resource();
else
sleep(a_short_while);
}
}
Run Code Online (Sandbox Code Playgroud)
我们现在不打击CPU,所以我们解决了1和4,但是a_short_while
当资源准备好或者我们被要求退出时,我们可以不必要地等待.
第三种选择是对资源进行阻塞读取:
void class::run()
{
while(!exit_flag)
{
obtain_resource();
use_resource();
}
}
Run Code Online (Sandbox Code Playgroud)
这将优雅地满足1,2和4,但是现在如果资源不可用,我们不能要求线程退出.
只要能够实现CPU使用率和响应性之间的权衡,最好的方法似乎是第二个,睡眠时间短.然而,这似乎仍然不是最理想的,对我来说也不优雅.这似乎是一个常见的问题需要解决.有更优雅的方式来解决它吗?有没有一种方法可以满足所有这四个要求?
这取决于线程正在访问的资源的细节,但基本上以最小的延迟有效地执行它,资源需要提供用于执行可中断阻塞等待的API.
在POSIX系统上,如果您使用的资源是文件或文件描述符(包括套接字),则可以使用select(2)
或poll(2)
系统调用来执行此操作.为了允许等待被抢占,您还可以创建一个可以写入的虚拟管道.
例如,以下是如何等待文件描述符或套接字准备就绪或代码被中断的方法:
// Dummy pipe used for sending interrupt message
int interrupt_pipe[2];
int should_exit = 0;
void class::run()
{
// Set up the interrupt pipe
if (pipe(interrupt_pipe) != 0)
; // Handle error
int fd = ...; // File descriptor or socket etc.
while (!should_exit)
{
// Set up a file descriptor set with fd and the read end of the dummy
// pipe in it
fd_set fds;
FD_CLR(&fds);
FD_SET(fd, &fds);
FD_SET(interrupt_pipe[1], &fds);
int maxfd = max(fd, interrupt_pipe[1]);
// Wait until one of the file descriptors is ready to be read
int num_ready = select(maxfd + 1, &fds, NULL, NULL, NULL);
if (num_ready == -1)
; // Handle error
if (FD_ISSET(fd, &fds))
{
// fd can now be read/recv'ed from without blocking
read(fd, ...);
}
}
}
void class::interrupt()
{
should_exit = 1;
// Send a dummy message to the pipe to wake up the select() call
char msg = 0;
write(interrupt_pipe[0], &msg, 1);
}
class::~class()
{
// Clean up pipe etc.
close(interrupt_pipe[0]);
close(interrupt_pipe[1]);
}
Run Code Online (Sandbox Code Playgroud)
如果你在Windows上,该select()
函数仍适用于套接字,但仅适用于套接字,因此您应该安装use WaitForMultipleObjects
来等待资源句柄和事件句柄.例如:
// Event used for sending interrupt message
HANDLE interrupt_event;
int should_exit = 0;
void class::run()
{
// Set up the interrupt event as an auto-reset event
interrupt_event = CreateEvent(NULL, FALSE, FALSE, NULL);
if (interrupt_event == NULL)
; // Handle error
HANDLE resource = ...; // File or resource handle etc.
while (!should_exit)
{
// Wait until one of the handles becomes signaled
HANDLE handles[2] = {resource, interrupt_event};
int which_ready = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
if (which_ready == WAIT_FAILED)
; // Handle error
else if (which_ready == WAIT_OBJECT_0))
{
// resource can now be read from without blocking
ReadFile(resource, ...);
}
}
}
void class::interrupt()
{
// Signal the event to wake up the waiting thread
should_exit = 1;
SetEvent(interrupt_event);
}
class::~class()
{
// Clean up event etc.
CloseHandle(interrupt_event);
}
Run Code Online (Sandbox Code Playgroud)
obtain_ressource()
如果您的函数支持超时值,您将获得有效的解决方案:
while(!exit_flag)
{
obtain_resource_with_timeout(a_short_while);
if (resource_ready)
use_resource();
}
Run Code Online (Sandbox Code Playgroud)
这有效地将sleep()
与obtain_ressurce()
调用结合起来。