lock 类
std::lock_guard,与 mutex RAII 相关,方便线程对互斥量上锁。
std::unique_lock,与 mutex RAII 相关,方便线程对互斥量上锁,但提供了更好的上锁和解锁控制。
std::lock_guard
在定义时构造函数中会lock,析构函数会自动unlock。
使用了lock_guard后就不应该对mutex再使用lock或unlock了。
#include <stdio.h>
#include <stdlib.h>
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex
int a = 0;
std::mutex mtx;
void thread_task1()
{
for (int i = 0; i < 100000; ++i)
{
std::lock_guard<std::mutex> lock(mtx);
//mtx.lock();
++a;
//mtx.unlock();
}
}
void thread_task2()
{
for (int i = 0; i < 100000; ++i)
{
std::lock_guard<std::mutex> lock(mtx);
//mtx.lock();
++a;
//mtx.unlock();
}
}
int main()
{
std::thread t1(thread_task1);
std::thread t2(thread_task2);
t1.join();
t2.join();
std::cout << a << std::endl;
return 0;
}
其他类型
std::once_flag
std::adopt_lock_t
std::defer_lock_t
std::try_to_lock_t
std::lock函数
可以同时对多个互斥量上锁如果互斥量中有一个没锁住,那么它会解锁已锁住的互斥量,不断尝试同时锁住所有的互斥量,所以这个函数的结果要么是所有互斥量都锁住了要么时所有互斥量一个都没锁住。
比如C++多线程std::mutex中最后讲到的死锁问题,就可以用这个函数解决。
#include <stdio.h>
#include <stdlib.h>
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex
int a = 0;
std::mutex mtx1;
std::mutex mtx2;
void thread_task1()
{
for (int i = 0; i < 100000; ++i)
{
std::lock(mtx1, mtx2);
//mtx1.lock();
//mtx2.lock();
++a;
mtx2.unlock();
mtx1.unlock();
}
}
void thread_task2()
{
for (int i = 0; i < 100000; ++i)
{
std::lock(mtx1, mtx2);
//mtx1.lock();
//mtx2.lock();
++a;
mtx1.unlock();
mtx2.unlock();
}
}
int main()
{
std::thread t1(thread_task1);
std::thread t2(thread_task2);
t1.join();
t2.join();
std::cout << a << std::endl;
return 0;
}
但是std::lock锁住的锁不会自动释放锁。
下面介绍std::lock_guard的std::adopt_lock参数。
std::adopt_lock参数
此参数作用就是让std::lock_guard在构造函数中不调用mutex的lock函数。
此参数也可用于std::unique::lock,作用一样。
#include <stdio.h>
#include <stdlib.h>
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex
int a = 0;
std::mutex mtx1;
std::mutex mtx2;
void thread_task1()
{
for (int i = 0; i < 100000; ++i)
{
std::lock(mtx1, mtx2);
std::lock_guard<std::mutex> lock1(mtx1, std::adopt_lock);
std::lock_guard<std::mutex> lock2(mtx2, std::adopt_lock);
++a;
}
}
void thread_task2()
{
for (int i = 0; i < 100000; ++i)
{
std::lock(mtx1, mtx2);
std::lock_guard<std::mutex> lock1(mtx1, std::adopt_lock);
std::lock_guard<std::mutex> lock2(mtx2, std::adopt_lock);
++a;
}
}
int main()
{
std::thread t1(thread_task1);
std::thread t2(thread_task2);
t1.join();
t2.join();
std::cout << a << std::endl;
return 0;
}
std::unique_lock
与 Mutex RAII 相关,方便线程对互斥量上锁,但提供了更好的上锁和解锁控制,它的功能比std::lock_guard更多,摩尔嗯不带参数的std::unique_lock与默认不带参数的std::lock_guard功能基本一致。
std::unique_lock使用std::adopt_lock参数前,互斥锁必须是已经被本线程锁定的。
使用std::try_to_lock参数前,互斥锁必须没有被本线程锁定的,后续可以owns_lock()函数判断是否已经获取到了锁的所有权。
使用std::defer_lock参数前,互斥锁必须没有被本线程锁定的,同时也不在构造函数中对锁进行锁定,使用这个参数定义的std::unique_lock,后续可以使用std::unique_lock的成员函数lock、unlock、try_lock,最后可以不解锁也可以解锁,不解锁的话析构仍会自动解锁。
release()函数会释放锁的所有权,而不是解锁,返回指向锁的指针。
#include <stdio.h>
#include <stdlib.h>
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex
int a = 0;
std::mutex mtx;
void thread_task1()
{
for (int i = 0; i < 100000; ++i)
{
std::unique_lock<std::mutex> lock(mtx);
++a;
}
}
void thread_task2()
{
for (int i = 0; i < 100000; ++i)
{
std::unique_lock<std::mutex> lock(mtx);
++a;
}
}
int main()
{
std::thread t1(thread_task1);
std::thread t2(thread_task2);
t1.join();
t2.join();
std::cout << a << std::endl;
return 0;
}
std::try_lock函数
尝试同时对多个互斥量上锁,注意与std::unique_lock的std::try_to_lock参数区分。
std::call_once函数
如果多个线程需要同时调用某个函数,call_once 可以保证多个线程对该函数只调用一次。需要配合std::one_flag类使用。
#include <stdio.h>
#include <stdlib.h>
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex
int a = 0;
std::once_flag g_flag;
void thread_task1()
{
++a;
}
void thread_task2()
{
std::call_once(g_flag, thread_task1);
}
int main()
{
std::thread t1(thread_task2);
std::thread t2(thread_task2);
t1.join();
t2.join();
std::cout << a << std::endl;
return 0;
}
当有多个线程调用的某个函数中有func,可以采用上述方法让该函数中的func只被执行一次。
thread_task2函数虽然被执行了两次,但是thread_task1只被执行了一次。