解除对另一个线程的阻塞
Posted
技术标签:
【中文标题】解除对另一个线程的阻塞【英文标题】:Unblocking a thread from another thread 【发布时间】:2019-03-10 19:55:50 【问题描述】:我正在寻找一种能够在从网络接收数据的同时向终端输入数据的方法。
为此,我创建了一个新线程 dataCapture
(std::thread
),它将使用 std::cin
获取输入。主线程是接收者。
当网络发送字符串“end”时程序需要退出。
这是简化的代码:
void dataCapture()
while (! quit)
std::string data;
std::cout << "Enter data: ";
std::cin >> data;
bool quit=false;
int main()
// socket creation
// connection to server
const std::string quit_value = "end";
std::thread datacapture_T(dataCapture);
while (! quit)
char recep[1024];
recv(sd, recep, sizeof(recep),0);
if (recep == quit_value)
quit=true;
datacapture_T.join();
return 0;
这不起作用,因为在收到"end"
后,main
阻塞了 datacapture_T.join()
,因为 dataCapture
在 std::cin
调用中被阻塞了。
是否可以从 main 解除阻塞 dataCapture
线程?
如果没有,是否可以强行杀死它?
ps:为了简化,我没有包括锁。
感谢您的帮助!
【问题讨论】:
尝试关闭std::cin
您可以将 quit 作为引用(必须在使用 std::ref 的线程中显式执行)传递给线程方法,然后当在主线程中更改时,它将生成线程方法退出。
@PaulSanders 我该怎么做对不起?你的意思是关闭 stdin 吗?如果是,程序实际上在加入后调用了另一个使用std::cin
的函数,那么我可以重新打开它吗?
@RasmusDall 我不认为我理解你的想法对不起。问题是线程在std::cin
上被阻塞,而不是它无法检测到quit
更改了值
cin
不是为此而构建的。为了获得最佳结果,您必须使用允许终止或超时的系统特定调用。一个例子:***.com/questions/18552029/…
【参考方案1】:
无论在理论上还是在实践中,都没有以编程方式杀死线程的干净方法。
以编程方式解除阻塞和关闭等待输入(任何类型)的线程的标准方法是使用select()
和pipe()
:
select()
创建一个文件描述符集,其中包含您的输入文件描述符和read()
和pipe()
。
不要对cin
执行阻塞读取,而是对select()
执行阻塞调用。
如果select()
告诉您可以从pipe()
中读取某些内容,请退出线程。您可以清理所有资源并完成所有处理,因为您正在检测线程中的终止请求。这是关键。
如果select()
告诉您可以从 cin 中读取某些内容,请阅读并处理它。
来自任何其他线程,例如在 main() 结束时:
每当您想终止线程时,将一个字节(值无关紧要)写入pipe()
。
join()
线程。
您认为这种方法听起来很武断且复杂吗?这是!然而,这是一种常见的做法,你可以依赖它来工作,因为它被许多框架和程序使用。
还有一些不太受欢迎的替代方案,例如轮询输入(非阻塞)和检查停止标志。也许最有用的替代方法是根本不使用任何其他线程来获取输入,并为所有 I/O 使用单线程事件驱动系统,而只在其他线程中进行内部处理。
线程不是实现 I/O 并发的有用机制。线程对于创建 CPU 处理并发很有用。
【讨论】:
效果很好,谢谢!我还尝试了一种方法,其中 select 在while(!quit)
循环中并且每 1 秒超时一次,这样:如果标准输入上有输入,它会处理它,否则它会在检查 !quit
后进行下一次迭代。那是不是性能较差?无限超时的 select 调用是否在内部有 while (true)
?
@Jonas:select()
挂起线程并允许其他线程运行直到有事情要做,所以这是最高效的方式,因为它允许其他线程进程在那里运行无事可做。在内部,它不使用轮询,而是直接使用内部操作系统事件。但是很多程序使用 1 秒(左右)的超时作为最后的手段,以防 pipe() 机制失败,实际上对于简单的程序,1 秒的超时更好,因为实现它的代码要少得多,并且通常 1s 最坏情况下的终止延迟是可以接受的。【参考方案2】:
你可以用ctrl-z
关闭std::cin
。
解决此问题的另一种方法是进入您的dataCapture()
并通过执行break
来检查其中的quit_value
。
编辑:考虑使用原子以及尽可能明确定义并发线程的读/写。不要认为它增加了太多的复杂性
【讨论】:
以上是关于解除对另一个线程的阻塞的主要内容,如果未能解决你的问题,请参考以下文章
网络I/O模型--04非阻塞模式(解除accept() read()方法阻塞)的基础上加入多线程技术
网络I/O模型--03非阻塞模式(ServerSocket与Socket的超时处理)--解除accept() read()方法阻塞