condition_variables 有什么更好的选择
What could be a better for condition_variables
我正在尝试制作一个看起来像这样的多线程函数:
namespace { // Anonymous namespace instead of static functions.
std::mutex log_mutex;
void Background() {
while(IsAlive){
std::queue<std::string> log_records;
{
// Exchange data for minimizing lock time.
std::unique_lock lock(log_mutex);
logs.swap(log_records);
}
if (log_records.empty()) {
Sleep(200);
continue;
}
while(!log_records.empty()){
ShowLog(log_records.front());
log_records.pop();
}
}
}
void Log(std::string log){
std::unique_lock lock(log_mutex);
logs.push(std::move(log));
}
}
我使用 Sleep 来防止 CPU 由于不断循环而导致的高使用率,即使日志为空也是如此。但这有一个非常明显的缺点,它会分批打印日志。我试图通过使用条件变量来解决这个问题,但问题是如果短时间内有太多日志,那么 cv 会停止并多次唤醒,导致更多 CPU 使用。现在我该怎么做才能解决这个问题?
您可以假设每秒可能有很多调用记录。
我可能会考虑为此使用计数信号量:
- 信号量将记录日志中消息的数量(最初为零)。
- 日志客户端将写入一条消息,并通过释放信号量将消息数加一。
- 日志服务器会获取信号量,阻塞直到日志中有任何消息,然后将消息数减一。
通知:
- 日志客户端获得
logs
队列锁,推送消息,然后才释放信号量。
- 日志服务器可以在获得
logs
队列锁之前进行acquire;即使有更多的读者,这也是可能的。例如:日志队列中的 1 条消息,服务器 1 执行获取,服务器 2 执行获取并阻塞,因为信号量计数为 0,服务器 1 继续并获得 logs
队列锁...
#include <algorithm> // for_each
#include <chrono> // chrono_literasl
#include <future> // async, future
#include <iostream> // cout
#include <mutex> // mutex, unique_lock
#include <queue>
#include <semaphore> // counting_semaphore
#include <string>
#include <thread> // sleep_for
#include <vector>
std::mutex mtx{};
std::queue<std::string> logs{};
std::counting_semaphore c_semaphore{ 0 };
int main()
{
auto log = [](std::string message) {
std::unique_lock lock{ mtx };
logs.push(std::move(message));
c_semaphore.release();
};
auto log_client = [&log]() {
using namespace std::chrono_literals;
static size_t s_id{ 1 };
size_t id{ s_id++ };
for (;;)
{
log(std::to_string(id));
std::this_thread::sleep_for(id * 100ms);
}
};
auto log_server = []() {
for (;;)
{
c_semaphore.acquire();
std::unique_lock lock{ mtx };
std::cout << logs.front() << " ";
logs.pop();
}
};
std::vector<std::future<void>> log_clients(10);
std::for_each(std::begin(log_clients), std::end(log_clients),
[&log_client](auto& lc_fut) {
lc_fut = std::async(std::launch::async, log_client);
});
auto ls_fut{ std::async(std::launch::async, log_server) };
std::for_each(std::begin(log_clients), std::end(log_clients),
[](auto& lc_fut) { lc_fut.wait(); });
ls_fut.wait();
}
我正在尝试制作一个看起来像这样的多线程函数:
namespace { // Anonymous namespace instead of static functions.
std::mutex log_mutex;
void Background() {
while(IsAlive){
std::queue<std::string> log_records;
{
// Exchange data for minimizing lock time.
std::unique_lock lock(log_mutex);
logs.swap(log_records);
}
if (log_records.empty()) {
Sleep(200);
continue;
}
while(!log_records.empty()){
ShowLog(log_records.front());
log_records.pop();
}
}
}
void Log(std::string log){
std::unique_lock lock(log_mutex);
logs.push(std::move(log));
}
}
我使用 Sleep 来防止 CPU 由于不断循环而导致的高使用率,即使日志为空也是如此。但这有一个非常明显的缺点,它会分批打印日志。我试图通过使用条件变量来解决这个问题,但问题是如果短时间内有太多日志,那么 cv 会停止并多次唤醒,导致更多 CPU 使用。现在我该怎么做才能解决这个问题? 您可以假设每秒可能有很多调用记录。
我可能会考虑为此使用计数信号量:
- 信号量将记录日志中消息的数量(最初为零)。
- 日志客户端将写入一条消息,并通过释放信号量将消息数加一。
- 日志服务器会获取信号量,阻塞直到日志中有任何消息,然后将消息数减一。
通知:
- 日志客户端获得
logs
队列锁,推送消息,然后才释放信号量。 - 日志服务器可以在获得
logs
队列锁之前进行acquire;即使有更多的读者,这也是可能的。例如:日志队列中的 1 条消息,服务器 1 执行获取,服务器 2 执行获取并阻塞,因为信号量计数为 0,服务器 1 继续并获得logs
队列锁...
#include <algorithm> // for_each
#include <chrono> // chrono_literasl
#include <future> // async, future
#include <iostream> // cout
#include <mutex> // mutex, unique_lock
#include <queue>
#include <semaphore> // counting_semaphore
#include <string>
#include <thread> // sleep_for
#include <vector>
std::mutex mtx{};
std::queue<std::string> logs{};
std::counting_semaphore c_semaphore{ 0 };
int main()
{
auto log = [](std::string message) {
std::unique_lock lock{ mtx };
logs.push(std::move(message));
c_semaphore.release();
};
auto log_client = [&log]() {
using namespace std::chrono_literals;
static size_t s_id{ 1 };
size_t id{ s_id++ };
for (;;)
{
log(std::to_string(id));
std::this_thread::sleep_for(id * 100ms);
}
};
auto log_server = []() {
for (;;)
{
c_semaphore.acquire();
std::unique_lock lock{ mtx };
std::cout << logs.front() << " ";
logs.pop();
}
};
std::vector<std::future<void>> log_clients(10);
std::for_each(std::begin(log_clients), std::end(log_clients),
[&log_client](auto& lc_fut) {
lc_fut = std::async(std::launch::async, log_client);
});
auto ls_fut{ std::async(std::launch::async, log_server) };
std::for_each(std::begin(log_clients), std::end(log_clients),
[](auto& lc_fut) { lc_fut.wait(); });
ls_fut.wait();
}