如何在 while 循环中使用条件变量进行互斥锁?

How to do mutex lock with a conditional variable in while loop?

我有两个函数,一个是 运行 serveRunForever,另一个是 shutDown。

Server::runForever ()
{
while(!doStop_)
{
unique_lock<mutex> lck(stop);
//Do something, it should be looping in while loop
CV.wait(lck, [this]() {return doStop_;});
}

以上不是循环它开始等待 CV.wait()。

Server::shutDown()
{
scoped_lock lck(stop);
doStop_ = true;
CV.notify_one();
}

运行Forever 没有循环,我想一直循环直到调用关机。帮我解决这个问题。我非常感谢对此的任何线索和建议。

第 1 部分

我对您的程序进行了修改,使其现在可以运行。主要是我不得不制作 doStop_ = true; 而不是 doStop_ = false;,因为根据英语程序的逻辑,当 do-stop 变为真时应该停止。

您还必须按照我在下面的代码中显示的方式放置互斥保护,即第一个锁在 while 循环之外,第二个标志设置锁的范围应该在通知调用之外。事实上,如果您将 wait 与谓词一起使用,您实际上不需要 while 循环,如 std docs.

中所述

这里是模拟工作过程:

Try it online!

#include <condition_variable>
#include <thread>
#include <mutex>
#include <iostream>
#include <chrono>

class Server {
public:
    void runForever();
    void shutDown();
private:
    std::condition_variable CV;
    std::mutex stop;
    bool doStop_ = false;
};

void Server::runForever() {
    std::unique_lock<std::mutex> lck(stop);
    while (!doStop_)
        CV.wait(lck, [this]() { return doStop_; });
}

void Server::shutDown() {
    {
        std::lock_guard<std::mutex> lck(stop);
        doStop_ = true;
    }
    CV.notify_one();
}

int main() {
    auto const time_begin = std::chrono::high_resolution_clock::now();
    auto TimeElapsed = [&]{
        return double(std::chrono::duration_cast<std::chrono::milliseconds>(
            std::chrono::high_resolution_clock::now() - time_begin).count()) / 1000;
    };

    Server server;
    std::this_thread::sleep_for(std::chrono::milliseconds(200));
    std::thread t0([&]{
        std::cout << "Server started at " << TimeElapsed() << " sec" << std::endl;
        server.runForever();
        std::cout << "Server stopped at " << TimeElapsed() << " sec" << std::endl;
    });
    std::this_thread::sleep_for(std::chrono::milliseconds(1500));
    server.shutDown();
    t0.join();
}

输出:

Server started at 0.2 sec
Server stopped at 1.7 sec

第 2 部分

如果您需要在 while 循环内进行一些计算,而不仅仅是等待,那么条件变量不适合您。您应该改用 std::atomic bool ,或者仅使用常规 bool 就足够了。 Atomic 允许立即传播 bool 变量的更改值。请参阅下面的代码:

Try it online!

#include <atomic>
#include <thread>
#include <mutex>
#include <iostream>
#include <chrono>

class Server {
public:
    void runForever();
    void shutDown();
private:
    std::atomic<bool> doStop_ = false;
};

void Server::runForever() {
    while (!doStop_) {
        std::cout << "Doing computation..." << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(200));
    }
}

void Server::shutDown() {
    doStop_ = true;
}

int main() {
    auto const time_begin = std::chrono::high_resolution_clock::now();
    auto TimeElapsed = [&]{
        return double(std::chrono::duration_cast<std::chrono::milliseconds>(
            std::chrono::high_resolution_clock::now() - time_begin).count()) / 1000;
    };

    Server server;
    std::this_thread::sleep_for(std::chrono::milliseconds(200));
    std::thread t0([&]{
        std::cout << "Server started at " << TimeElapsed() << " sec" << std::endl;
        server.runForever();
        std::cout << "Server stopped at " << TimeElapsed() << " sec" << std::endl;
    });
    std::this_thread::sleep_for(std::chrono::milliseconds(900));
    server.shutDown();
    t0.join();
}

输出:

Server started at 0.21 sec
Doing computation...
Doing computation...
Doing computation...
Doing computation...
Doing computation...
Server stopped at 1.21 sec

第 3 部分

您还可以与 atomic bool 一起在 while 循环中使用条件变量以将其用于暂停目的,而不是 std::this_thread::sleep_for()

条件变量将有助于停止前的最后一次迭代,将有助于在整个暂停完成之前提前终止。

您可以在下面的控制台输出示例中看到,与 第 2 部分 示例中的 1.2 sec 相比,服务器在 1.1 sec 处提前停止我的回答。

Try it online!

#include <condition_variable>
#include <atomic>
#include <thread>
#include <mutex>
#include <iostream>
#include <chrono>

class Server {
public:
    void runForever();
    void shutDown();
private:
    std::atomic<bool> doStop_ = false;
    std::mutex mutex_;
    std::condition_variable CV;
};

void Server::runForever() {
    while (!doStop_) {
        std::cout << "Doing computation..." << std::endl;
        
        std::unique_lock<std::mutex> lck(mutex_);
        CV.wait_for(lck, std::chrono::milliseconds(200),
            [this]{ return bool(doStop_); });
    }
}

void Server::shutDown() {
    {
        std::lock_guard<std::mutex> lck(mutex_);
        doStop_ = true;
    }
    CV.notify_all();
}

int main() {
    auto const time_begin = std::chrono::high_resolution_clock::now();
    auto TimeElapsed = [&]{
        return double(std::chrono::duration_cast<std::chrono::milliseconds>(
            std::chrono::high_resolution_clock::now() - time_begin).count()) / 1000;
    };

    Server server;
    std::this_thread::sleep_for(std::chrono::milliseconds(200));
    std::thread t0([&]{
        std::cout << "Server started at " << TimeElapsed() << " sec" << std::endl;
        server.runForever();
        std::cout << "Server stopped at " << TimeElapsed() << " sec" << std::endl;
    });
    std::this_thread::sleep_for(std::chrono::milliseconds(900));
    server.shutDown();
    t0.join();
}

输出:

Server started at 0.203 sec
Doing computation...
Doing computation...
Doing computation...
Doing computation...
Doing computation...
Server stopped at 1.1 sec