1.互斥锁:mutex
互斥锁保证了同一时刻只有一个线程操作资源。
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx; // 全局互斥锁
int counter = 0; // 共享数据
void increment() {
for (int i = 0; i < 10000; ++i) {
std::lock_guard<std::mutex> guard(mtx); // 自动加锁,作用域结束时自动解锁
counter++;
}
}
int main() {
std::thread t1(increment);
std::thread t2(increment);
t1.join(); // 等待线程1完成
t2.join(); // 等待线程2完成
std::cout << "Counter: " << counter << std::endl;
return 0;
}
将以下代码注释,运行程序可以观察到多线程同时操作变量counter产生的错误。
std::lock_guard<std::mutex> guard(mtx); // 自动加锁,作用域结束时自动解锁
要点:
- 首先,这在一个局部作用域内, std::lock_guard 在构造时,会调用 g_mutex->lock() 方法;
- 局部作用域代码结束后, std:;lock_guard 的析构函数会被调用,函数中会调用 g_mutex->unlock() 方法。
这样就实现了加锁和解锁的过程,为什么不直接调用加锁解锁方法呢?
这是因为如果加锁和解锁中间的代码出现了问题,导致线程函数异常退出,那么这个锁就一直无法得到释放,其它线程处理的不好的话,就会造成死锁了。
2.对加锁时间的思考
#include <iostream>
#include <mutex>
#include <thread>
std::mutex mutex0;
int g_count = 0;
void func() {
for (int i = 0; i < 100000; i++) {
/*for循环内部加锁,每一次自加操作是互斥的,但是for循环期间不是互斥的*/
std::lock_guard<std::mutex> guard(mutex0);
g_count++;
if (i == 90000) {
std::cout << g_count << std::endl; //输出结果随机
}
}
}
int main() {
std::thread t1(func);
std::thread t2(func);
t1.join();
t2.join();
std::cout << g_count << std::endl;
}
#include <iostream>
#include <mutex>
#include <thread>
std::mutex mutex0;
int g_count = 0;
void func() {
/*func内部加锁,整个func运行期间互斥*/
std::lock_guard<std::mutex> guard(mutex0);
for (int i = 0; i < 100000; i++) {
g_count++;
if (i == 90000) {
std::cout << g_count << std::endl; //输出结果固定
}
}
}
int main() {
std::thread t1(func);
std::thread t2(func);
t1.join();
t2.join();
std::cout << g_count << std::endl;
}
3.线程同步:条件变量
/*线程1执行到一半释放互斥锁,然后线程2加锁执行到结束释放互斥锁,并通过条件变量告诉线程1*/
#include <iostream>
#include <mutex>
#include <thread>
#include <condition_variable>
std::mutex g_mutex; // 用到的全局锁
std::condition_variable g_cond; // 用到的条件变量
int g_i = 0;
bool g_running = false;
void ThreadFunc1() { // 线程执行函数
std::cout << "thread1 begin" << std::endl;
std::unique_lock<std::mutex> lock(g_mutex); // 加锁
for (int i = 0; i < 100000; ++i) ++g_i;
std::cout << "thread1 g_i=" << g_i << std::endl;
while (!g_running) {
g_cond.wait(lock); // wait调用后,会先释放锁,之后进入等待状态;当其它进程调用通知激活后,会再次加锁
}
lock.unlock();
std::cout << "thread1 g_i=" << g_i << std::endl;
std::cout << "thread1 end" << std::endl;
}
void ThreadFunc2() {
std::cout << "thread2 begin" << std::endl;
{
std::lock_guard<std::mutex> lock(g_mutex);
g_running = true;
g_cond.notify_one(); // 通知其它线程
}
std::unique_lock<std::mutex> lock(g_mutex);
std::cout << "thread2 g_i=" << g_i << std::endl;
for (int i = 0; i < 100000; ++i) ++g_i;
std::cout << "thread2 g_i=" << g_i << std::endl;
lock.unlock();
std::cout << "thread2 end" << std::endl;
}
int main() {
std::thread t1(ThreadFunc1);
std::thread t2(ThreadFunc2);
t1.join(); // 等待线程t1结束
t2.join();
std::cout << "g_i = " << g_i << std::endl;
}