以下程序仍将输出交错为std::cout
。我通过std::cout
尝试add a std::mutex
to control access到std::lock_guard
,但still interleaves。
#include <iostream>
#include <chrono>
#include <thread>
#include <functional>
#include <mutex>
#include <condition_variable>
std::mutex global_mtx{};
class Timer {
public:
Timer(size_t time, const std::function<void(void)>& f) : time{std::chrono::milliseconds{time}}, f{f} {}
~Timer() { wait_thread.join(); }
private:
void wait_then_call()
{
std::unique_lock<std::mutex> lck{mtx};
for(int i{10}; i > 0; --i) {
{
std::lock_guard<std::mutex>{global_mtx};
std::cout << "Thread " << wait_thread.get_id() << " countdown at: " << '\t' << i << std::endl;
}
cv.wait_for(lck, time / 10);
}
f();
}
std::mutex mtx;
std::condition_variable cv{};
std::chrono::milliseconds time;
std::function <void(void)> f;
std::thread wait_thread{[this]() {wait_then_call(); }};
};
int main()
{
auto f = []() {std::lock_guard<std::mutex>{global_mtx}; std::cout << "---------------- I waited to print! ----------------" << std::endl; };
Timer t1{3'000,f};
Timer t2{6'000,f};
Timer t3{2'000,f};
Timer t4{1'000,f};
}
我是否需要通过单独的类或专用线程来控制访问?
答案 0 :(得分:3)
你的问题在这里:std::lock_guard<std::mutex>{global_mtx};
创建一个锁定保护并立即释放它。您需要创建一个变量来保存锁定,例如std::lock_guard<std::mutex> lock{global_mtx};
。
答案 1 :(得分:3)
防止忘记命名锁定的一种方法是创建一个可以用作io操纵器的锁定对象:
#include <iostream>
#include <chrono>
#include <thread>
#include <functional>
#include <mutex>
#include <condition_variable>
std::mutex global_mtx{};
struct lockio
{
lockio(std::mutex& m) : lock_(m) {}
std::unique_lock<std::mutex> lock_;
};
std::ostream& operator<<(std::ostream& os, const lockio&) {
return os;
}
class Timer {
public:
Timer(size_t time, const std::function<void(void)>& f) : time{std::chrono::milliseconds{time}}, f{f} {}
~Timer() { wait_thread.join(); }
private:
void wait_then_call()
{
std::unique_lock<std::mutex> lck{mtx};
for(int i{10}; i > 0; --i) {
{
std::cout << lockio(global_mtx) << "Thread " << wait_thread.get_id() << " countdown at: " << '\t' << i << std::endl;
}
cv.wait_for(lck, time / 10);
}
f();
}
std::mutex mtx;
std::condition_variable cv{};
std::chrono::milliseconds time;
std::function <void(void)> f;
std::thread wait_thread{[this]() {wait_then_call(); }};
};
int main()
{
auto f = []() { std::cout << lockio(global_mtx) << "---------------- I waited to print! ----------------" << std::endl; };
Timer t1{3'000,f};
Timer t2{6'000,f};
Timer t3{2'000,f};
Timer t4{1'000,f};
}
另一种(可能更好)的方法是创建一个小帮助模板函数来包装受保护的操作:
#include <iostream>
#include <thread>
#include <condition_variable>
std::mutex global_mtx{};
template<class Mutex, class F>
decltype(auto) with_lock(Mutex &m, F &&f) {
std::lock_guard<Mutex> lock(m);
return f();
};
class Timer {
public:
Timer(size_t time, const std::function<void(void)> &f) : time{std::chrono::milliseconds{time}}, f{f} {}
~Timer() { wait_thread.join(); }
private:
void wait_then_call() {
std::unique_lock<std::mutex> lck{mtx};
for (int i{10}; i > 0; --i) {
with_lock(global_mtx, [&] {
std::cout << "Thread " << wait_thread.get_id() << " countdown at: " << '\t' << i << std::endl;
});
cv.wait_for(lck, time / 10);
}
f();
}
std::mutex mtx;
std::condition_variable cv{};
std::chrono::milliseconds time;
std::function<void(void)> f;
std::thread wait_thread{[this]() { wait_then_call(); }};
};
int main() {
auto f = []() {
with_lock(global_mtx, []
{
std::cout << "---------------- I waited to print! ----------------" << std::endl;
});
};
Timer t1{3'000, f};
Timer t2{6'000, f};
Timer t3{2'000, f};
Timer t4{1'000, f};
}
另一种方式:
#include <iostream>
#include <thread>
#include <condition_variable>
struct locked {
std::ostream& cout() const { return std::cout; }
std::ostream& cerr() const { return std::cerr; }
private:
static std::mutex& mutex() {
static std::mutex stdio_mutex;
return stdio_mutex;
}
std::unique_lock<std::mutex> lock_{mutex()};
};
class Timer {
public:
Timer(size_t time, const std::function<void(void)> &f) : time{std::chrono::milliseconds{time}}, f{f} {}
~Timer() { wait_thread.join(); }
private:
void wait_then_call() {
std::unique_lock<std::mutex> lck{mtx};
for (int i{10}; i > 0; --i) {
locked().cout() << "Thread " << wait_thread.get_id() << " countdown at: " << '\t' << i << std::endl;
cv.wait_for(lck, time / 10);
}
f();
}
std::mutex mtx;
std::condition_variable cv{};
std::chrono::milliseconds time;
std::function<void(void)> f;
std::thread wait_thread{[this]() { wait_then_call(); }};
};
int main() {
auto f = []() {
locked().cout() << "---------------- I waited to print! ----------------" << std::endl;
};
Timer t1{3'000, f};
Timer t2{6'000, f};
Timer t3{2'000, f};
Timer t4{1'000, f};
}
答案 2 :(得分:0)
您创建了四个Timer对象,每个对象都有自己唯一的互斥对象。因此,当t2运行其线程时,它会锁定自己的互斥锁,因为t1在开始循环之前锁定了不同的互斥锁。