C++:带有"std::lock_guard"的互斥锁是否足以同步两个"std::thre
C++: Is a mutex with `std::lock_guard` enough to synchronize two `std::thread`s?
我的问题基于下面的C++代码示例
#include <chrono>
#include <thread>
#include <mutex>
#include <iostream>
class ClassUtility
{
public:
ClassUtility() {}
~ClassUtility() {}
void do_something() {
std::cout << "do something called" << std::endl;
using namespace std::chrono_literals;
std::this_thread::sleep_for(1s);
}
};
int main (int argc, const char* argv[]) {
ClassUtility g_common_object;
std::mutex g_mutex;
std::thread worker_thread_1([&](){
std::cout << "worker_thread_1 started" << std::endl;
for (;;) {
std::lock_guard<std::mutex> lock(g_mutex);
std::cout << "worker_thread_1 looping" << std::endl;
g_common_object.do_something();
}
});
std::thread worker_thread_2([&](){
std::cout << "worker_thread_2 started" << std::endl;
for (;;) {
std::lock_guard<std::mutex> lock(g_mutex);
std::cout << "worker_thread_2 looping" << std::endl;
g_common_object.do_something();
}
});
worker_thread_1.join();
worker_thread_2.join();
return 0;
}
这更像是一个让我理解清楚的问题,而不是获得所需的std::condition_variable
的示例用法。
我有 2 个C++std::thread
以main
方法启动。它是osx
上的控制台应用程序。所以使用叮当声编译它。两个线程都使用一个公共对象ClassUtility
调用方法做一些繁重的任务。对于此示例代码来解释这种情况,两个线程都运行无限循环并仅在以下情况下关闭 该应用程序关闭,即当我在控制台上按ctrl+c
时。
想知道:
如果我在std::mutex
上使用std::lock_guard
来同步或保护对ClassUtility
common_obejct
的调用是否正确。不知何故,我似乎 陷入这种">只是互斥体方法"的麻烦。如果我使用互斥锁锁定保护循环,则不会启动任何线程。此外,我有时会出现段错误。这是因为它们是拉姆达吗? 分配给每个线程 ?
在 2 个线程或 lambda 之间使用std::condition_variable
来发出信号并同步它们是否更好?如果是,那么如何使用std::condition_variable
在拉姆达斯之间?
注意:由于问题只是寻找信息,因此此处提供的代码可能无法编译。只是为了提供一个真实的场景
你的代码是安全的
请记住,lock_guard只是调用.lock()
并将调用注入到块的末尾.unlock()
。所以
{
std::lock_guard<std::mutex> lock(g_mutex);
std::cout << "worker_thread_1 looping" << std::endl;
g_common_object.do_something();
}
基本等同于:
{
g_mutex.lock();
std::cout << "worker_thread_1 looping" << std::endl;
g_common_object.do_something();
g_mutex.unlock();
}
除了:
- 即使通过异常离开块,也会调用解锁,并且
- 它确保您不会忘记打电话。
您的代码不是并行的
您相互排除每个线程中的所有循环体。两个线程实际上可以并行执行的操作已不复存在。使用线程的要点是当每个线程都可以处理单独的对象集(并且只读取公共对象)时,因此不必锁定它们。
在示例代码中,您实际上应该只锁定公共对象上的工作;std::cout
本身是线程安全的。所以:
{
std::cout << "worker_thread_1 looping" << std::endl;
{
std::lock_guard<std::mutex> lock(g_mutex);
g_common_object.do_something();
// unlocks here, because lock_guard injects unlock at the end of innermost scope.
}
}
我想你尝试编写的实际代码确实有一些实际并行做的事情;只是要记住一件事。
不需要条件变量
条件变量适用于您需要一个线程等待另一个线程执行某些特定操作的情况。在这里,您只是确保两个线程不会同时修改对象,并且为此mutex
是足够且合适的。
你的代码永远不会终止,除了我挑不出毛病。
正如其他人指出的那样,它几乎没有提供并行性的机会,因为当互斥锁锁定到休眠线程时会发生长时间的休眠。
这是一个简单的版本,它通过在循环上放置任意有限限制来终止。
也许你不明白join()
是做什么的? 它是当前线程(正在执行join()
),直到连接的线程结束。但如果它没有结束,当前线程也不会结束。
#include <chrono>
#include <thread>
#include <mutex>
#include <iostream>
class ClassUtility
{
public:
ClassUtility() {}
~ClassUtility() {}
void do_something() {
std::cout << "do something called" << std::endl;
using namespace std::chrono_literals;
std::this_thread::sleep_for(1s);
}
};
int main (int argc, const char* argv[]) {
ClassUtility g_common_object;
std::mutex g_mutex;
std::thread worker_thread_1([&](){
std::cout << "worker_thread_1 started" << std::endl;
for (int i=0;i<10;++i) {
std::lock_guard<std::mutex> lock(g_mutex);
std::cout << "worker_thread_1 looping " << i << std::endl;
g_common_object.do_something();
}
});
std::thread worker_thread_2([&](){
std::cout << "worker_thread_2 started" << std::endl;
for (int i=0;i<10;++i) {
std::lock_guard<std::mutex> lock(g_mutex);
std::cout << "worker_thread_2 looping " << i << std::endl;
g_common_object.do_something();
}
});
worker_thread_1.join();
worker_thread_2.join();
return 0;
}
- 使用std::multimap迭代器创建std::list
- C++中std::resize(n)和std::shrink_to_fit之间的区别
- 来自 std::list 的迭代器 .end() 按预期返回"0xcdcdcdcdcdcdcdcd"但 .begin()
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 如何导出包含具有"std::unique_ptr"值的"std::map"属性的
- 从持续时间构造std::chrono::system_clock::time_point
- std::具有相同基类的类的变体
- std::向量与传递值的动态数组
- 使用std::vector的OpenCL矩阵乘法
- std::map<struct,struct>::find 找不到匹配项,但是如果我循环通过 begin() 到 end(),我在那里看到匹配项
- std::condition_variable::wait()如何评估给定的谓词
- 如何获取std::result_of函数的返回类型
- std::原子加载和存储都需要吗
- 将对象移动到std::shared_ptr
- POCO::PostgreSQL:如何将std::vector支持添加到`Binder::bind`
- 使用一个考虑到std::map中键值的滚动或换行的键
- 如何从 std::atomic 中提取指针 T<T>?
- 为什么 std::unique 不调用 std::sort?
- 使用std::函数映射对象方法
- 如何检测我何时向可变参数函数传递"std::string"而不是"c_str()"