我有两个问题。
1)我想用无限循环启动一些函数,就像服务器一样工作,并在一个单独的线程中检查消息。但是我想在我想要的时候从父线程关闭它。在这种情况下,我很混淆如何std::future
或std::condition_variable
。或者,创建一些全局变量并将其从父线程更改为true / false更好
2)我想要这样的东西。为什么这个例子在运行时崩溃了?
#include <iostream>
#include <chrono>
#include <thread>
#include <future>
std::mutex mu;
bool stopServer = false;
bool serverFunction()
{
while (true)
{
// checking for messages...
// processing messages
std::this_thread::sleep_for(std::chrono::seconds(1));
mu.lock();
if (stopServer)
break;
mu.unlock();
}
std::cout << "Exiting func..." << std::endl;
return true;
}
int main()
{
std::thread serverThread(serverFunction);
// some stuff
system("pause");
mu.lock();
stopServer = true;
mu.unlock();
serverThread.join();
}
答案 0 :(得分:4)
为什么这个例子在运行时崩溃?
当您离开线程的内部循环时,会使互斥锁处于锁定状态,因此如果再次使用该互斥锁,则父线程可能会被永久阻止。
你应该使用std::unique_lock
或类似的东西来避免这样的问题。
答案 1 :(得分:1)
您的互斥锁已锁定。不要在999/1000的情况下手动锁定互斥锁。
在这种情况下,您可以使用std::unique_lock<std::mutex>
创建一个可以避免此问题的RAII锁定持有者。只需在作用域中创建它,并将锁定区域结束于作用域的末尾。
{
std::unique_lock<std::mutex> lock(mu);
stopServer = true;
}
<{1>}和中的
main
{
std::unique_lock<std::mutex> lock(mu);
if (stopServer)
break;
}
中的。
现在在这种情况下,你的互斥锁毫无意义。去掉它。将serverFunction
替换为bool stopServer
,并从代码中删除对std::atomic<bool> stopServer
和mutex
的所有引用。
可以安全地从不同的线程读取/写入原子变量。
但是,您的代码仍在忙着等待。处理服务器处理消息的正确方法是保护消息队列的条件变量。然后,通过在消息队列中对停止服务器消息(或标志)进行前排队来停止它。
这导致服务器线程几乎不会经常唤醒和无意义地旋转。相反,它会阻塞条件变量(有一些虚假的唤醒,但很少见)并且只有在有新消息或者被告知要关闭时才会真正唤醒。
mu
我们使用template<class T>
struct cross_thread_queue {
void push( T t ) {
{
auto l = lock();
data.push_back(std::move(t));
}
cv.notify_one();
}
boost::optional<T> pop() {
auto l = lock();
cv.wait( l, [&]{ return halt || !data.empty(); } );
if (halt) return {};
T r = data.front();
data.pop_front();
return std::move(r); // returning to optional<T>, so we'll explicitly `move` here.
}
void terminate() {
{
auto l = lock();
data.clear();
halt = true;
}
cv.notify_all();
}
private:
std::mutex m;
std::unique_lock<std::mutex> lock() {
return std::unique_lock<std::mutex>(m);
}
bool halt = false;
std::deque<T> data;
std::condition_variable cv;
};
作为boost::optional
的返回类型 - 如果队列暂停,pop返回一个空的可选项。否则,它会阻塞,直到有数据。
您可以将其替换为可选的任何内容,即使是pop
,其中第一个元素表示是否有任何要返回的内容,或者std::pair<bool, T>
,或std::unique_ptr<T>
,或者无数其他选择。
std::experimental::optional