我在概念化
unique_lock
应该如何跨线程操作时遇到一些困难。我尝试制作一个快速示例来重新创建我通常会使用 condition_variable
的东西。
#include <mutex>
#include <thread>
using namespace std;
mutex m;
unique_lock<mutex>* mLock;
void funcA()
{
//thread 2
mLock->lock();//blocks until unlock?Access violation reading location 0x0000000000000000.
}
int _tmain(int argc, _TCHAR* argv[])
{
//thread 1
mLock = new unique_lock<mutex>(m);
mLock->release();//Allows .lock() to be taken by a different thread?
auto a = std::thread(funcA);
std::chrono::milliseconds dura(1000);//make sure thread is running
std::this_thread::sleep_for(dura);
mLock->unlock();//Unlocks thread 2's lock?
a.join();
return 0;
}
unique_lock
不应同时从多个线程访问。它并不是以这种方式设计为线程安全的。相反,多个 unique_lock
(局部变量)引用相同的全局 mutex
。只有 mutex
本身被设计为可以同时被多个线程访问。即便如此,我的陈述也不包括 ~mutex()
。
例如,我们知道
mutex::lock()
可以被多个线程访问,因为它的规范包括以下内容:
同步:同一对象上的先前
操作应与(4.7)此操作同步。unlock()
其中 synchronize with 是 4.7 [intro.multithread](及其子条款)中定义的术语。
release
是“解除互斥体关联而不解锁它”,这不太可能是您想要在该位置执行的操作。这基本上意味着您的 mutex
中不再有 unique_lock<mutex>
- 这将使其变得毫无用处 - 这可能是您获得“访问冲突”的原因。
编辑:对代码进行一些“按摩”并说服 g++ 4.6.3 执行我想要的操作(因此是
#define _GLIBCXX_USE_NANOSLEEP
),这是一个工作示例:
#define _GLIBCXX_USE_NANOSLEEP
#include <chrono>
#include <mutex>
#include <thread>
#include <iostream>
using namespace std;
mutex m;
void funcA()
{
cout << "FuncA Before lock" << endl;
unique_lock<mutex> mLock(m);
//thread 2
cout << "FuncA After lock" << endl;
std::chrono::milliseconds dura(500);//make sure thread is running
std::this_thread::sleep_for(dura); //this_thread::sleep_for(dura);
cout << "FuncA After sleep" << endl;
}
int main(int argc, char* argv[])
{
cout << "Main before lock" << endl;
unique_lock<mutex> mLock(m);
auto a = std::thread(funcA);
std::chrono::milliseconds dura(1000);//make sure thread is running
std::this_thread::sleep_for(dura); //this_thread::sleep_for(dura);
mLock.unlock();//Unlocks thread 2's lock?
cout << "Main After unlock" << endl;
a.join();
cout << "Main after a.join" << endl;
return 0;
}
不知道为什么你需要使用
new
来创建锁。当然 unique_lock<mutex> mlock(m);
应该可以解决问题(当然,mLock->
也相应地变为 mLock.
)。
锁只是一个自动防护装置,以安全、理智的方式操作互斥锁。
你真正想要的是这段代码:
std::mutex m;
void f()
{
std::lock_guard<std::mutex> lock(m);
// ...
}
这有效地“同步”了对
f
的调用,因为进入其中的每个线程都会阻塞,直到它设法获得互斥锁。
A
unique_lock
只是 lock_guard
的增强版:它可以被构造为解锁、移动(谢谢,@MikeVine),它本身就是一个“可锁定对象”,就像互斥体本身一样,所以它例如,可以在变量 std::lock(...)
中使用,以无死锁的方式同时锁定多个事物,并且它可以由 std::condition_variable
管理(谢谢,@syam)。
但是除非您有充分的理由使用
unique_lock
,否则更喜欢使用 lock_guard
。一旦您需要升级到unique_lock
,您就会知道原因。
作为旁注,上述答案跳过了互斥锁的立即锁定和延迟锁定之间的区别:
#include<mutex>
::std::mutex(mu);
auto MyFunction()->void
{
std::unique_lock<mutex> lock(mu); //Created instance and immediately locked the mutex
//Do stuff....
}
auto MyOtherFunction()->void
{
std::unique_lock<mutex> lock(mu,std::defer_lock); //Create but not locked the mutex
lock.lock(); //Lock mutex
//Do stuff....
lock.unlock(); //Unlock mutex
}
MyFunction() 显示广泛使用的立即锁,而 MyOtherFunction() 显示延迟锁。