多线程并发指的是在同一个进程中执行多个线程,线程是轻量级的进程,同一进程中的多个线程共享相同的地址空间,可以访问进程中的大部分数据,指针和引用可以在线程间进行传递。
简单创建使用
头文件:
#include <thread>
创建线程:
第一种
std::thread myThread (thread_fun); //创建线程myThread,线程中传入函数thread_fun
myThread.join();
---------------------------
第二种
std::thread myThread (thread_fun(100));
myThread.join();
---------------------------
第三种
std::thread (thread_fun,1).join();
- detach方式,启动的线程自主在后台运行,当前的代码继续往下执行,不等待新线程结束。
- join方式,等待启动的线程完成,才会继续执 执行下面的代码。
代码例:
#include <iostream>
#include <thread>
using namespace std;
void thread_1()
{
cout << "子线程1" << endl<<endl;
}
void thread_2(int x)
{
cout << "子线程2,x="<<x << endl<< endl;
}
void thread_3(int x)
{
cout << "子线程3,x=" <<x<< endl<< endl;
}
int main()
{
cout<<"--------------------------------------------"<<endl;
thread first(thread_1); // 开启线程,调用:thread_1()
thread second(thread_2, 2); // 开启线程,调用:thread_2(100)
thread third(thread_3, 3); // 开启第3个线程,共享thread_2函数。
first.join(); // 必须说明添加线程的方式
second.join();
third.join();
std::cout <<"+++++++++++\n这是主线程+\n+++++++++++\n\n";
std::cout << "子线程结束.\n"; // 必须join完成
cout<<"---------------------------------------------"<<endl;
return 0;
}
编译:g++ test.cc -o test -l pthread
运行:./test
结果:
可以发现执行多次,每次执行可能不相同,因为线程是并发进行的。
同时可以发现“这是主线程”永远在三个线程执行结果后面,因为线程的执行方式是join,必须要等线程执行完了之后才可以执行接下来的代码(非声明join或detach的代码)。
注:
可以使用joinable判断是join模式还是detach模式。
if (myThread.joinable()) foo.join()
this_thread
std::this_thread::get_id() 获取线程id
std::this_thread::yield() 放弃线程执行,回到就绪状态
std::this_thread::sleep_for(std::chrono::seconds(1)); 暂停1秒(sleep_until也可以达到类似的效果)
死锁
mutex
#include <iostream> // std::cout #include <thread> // std::thread #include <mutex> // std::mutex using namespace std; mutex mtx1; mutex mtx2; void print_block1 (int n, char c) { mtx1.lock(); for (int i=0; i<n; ++i) { cout << c; } cout << '\n'; mtx1.unlock(); } void print_block2 (int n, char c) { mtx1.lock(); for (int i=0; i<n; ++i) { cout << c; } cout << '\n'; mtx1.unlock(); } int main () { thread th1 (print_block1,300,'*');//线程1:打印* thread th2 (print_block2,300,'$');//线程2:打印$ th1.join(); th2.join(); return 0; }
因为两个打印程序使用的是同一个mutex,所以互斥,不会同时执行。如果把第二个函数的mutex变量换成mtx2就会出现下面交替打印的情况:
lock_gaurd
作用:为了防止在线程使用mutex加锁后异常退出导致死锁的问题,建议使用lock_guard代替mutex。
(1) 创建即加锁,作用域结束自动析构并解锁,无需手工解锁
(2) 不能中途解锁,必须等作用域结束才解锁
(3) 不能复制
格式:template<class Mutex> class lock_guard;
eg:lock_gurad<std::mutex> lock(mtx)
unique_lock
unique_lock不仅可以使用在简单的临界代码段的互斥操作中,还可以使用在函数调用过程中
std::unique_lock<std::mutex> munique(mlock,参数);
参数可以是:
std::try_to_lock:会判断当前mutex能否被lock,如果不能被lock,可以先去执行其他代码
std::unique_lock<std::mutex> munique(mlock, std::try_to_lock); if (munique.owns_lock() == true) { s += i; } else { // 执行一些没有共享内存的代码 } }
std::defer_lock: 这个参数表示暂时先不lock,之后手动去lock,但是使用之前也是不允许去lock。一般用来搭配unique_lock的成员函数去使用。
std::unique_lock<std::mutex> munique(mlock, std::defer_lock); munique.lock(); s += i; munique.unlock();
还有一个成员函数是try_lock,和上面的try_to_lock参数的作用差不多,判断当前是否能lock,如果不能,先去执行其他的代码并返回false,如果可以,进行加锁并返回true
release函数,解除unique_lock和mutex对象的联系,并将原mutex对象的指针返回出来。
std::unique_lock<std::mutex> munique(mlock); // 这里是自动lock std::mutex *m = munique.release(); s += i; m->unlock();
参考:C++多线程unique_lock详解 - 腾讯云开发者社区-腾讯云
简单的场景,不涉及线程通信时,可以使用 lock_guard, 但是涉及函数调用或线程通信时 使用 unique_lock
参考: