【一、多线程基础】
1. 线程函数传递对象、指针、引用
#include <iostream>
using namespace std;
#include "thread"
class Para {
public:
string name;
Para() { cout << "create Para" << endl; }
Para(const Para ¶) { cout << "Copy Para" << endl; }
~Para() { cout << "Drop Para" << endl; }
};
//传递引用
void threadRef(Para& para) {
cout << "threadRef para->name= " << para.name << endl;
}
//传递指针
void threadPtr(Para *para) {
cout << "threadPtr para->name= " << para->name << endl;
}
//传递对象
void threadPara(Para para) {
cout << "threadPara para.name= " << para.name << endl;
}
int main() {
//传引用
{
Para para;
para.name = "test Para name";
thread th;
th = thread(threadRef, ref(para));
th.join();
}
// //传指针
//{
// Para para;
// para.name = "test Para name";
// thread th;
// th = thread(threadPtr, ¶);
// th.join();
//}
//传对象 调用析构函数 1.定义一个对象 2.thread内部会将参数进行复制 3.thread回调threadPara()函数时,进行一次复制
//{
// Para para;
// para.name = "test Para name";
// thread th;
// th = thread(threadPara, para);
// th.join();
//}
return 0;
}
- 传对象结果
- 传指针结果
- 传引用结果
2. 线程jion()和detach()
- 当使用detach()函数时,主调线程继续运行,被调线程驻留后台运行,主调线程无法再取得该被调线程的控制权。当主调线程结束时,由运行时库负责清理与被调线程相关的资源。
- 当使用join()函数时,主调线程阻塞,等待被调线程终止,然后主调线程回收被调线程资源,并继续运行。
链接: 1
3. 成员函数作为线程入口
override 关键字,可以显式的在派生类中声明,哪些成员函数需要被重写,如果没被重写,则编译器会报错。
#include <iostream>
using namespace std;
#include "thread"
class XThread {
public:
virtual void Start() {
is_exit_ = false;
th_ = std::thread(&XThread::Main, this);
}
virtual void Wait() {
if (th_.joinable())
th_.join();
}
//通过主动调用stop来控制线程的退出
virtual void Stop() {
is_exit_ = true;
Wait();
}
bool is_exit() { return is_exit_; }
private:
virtual void Main() = 0;
std::thread th_;
bool is_exit_;
};
class TestXThread: public XThread {
public:
virtual void Main() override {
cout << "TestXThread Main" << endl;
while (!is_exit()) {
cout << "." << flush;
}
}
string name;
};
int main() {
TestXThread testXTread;
testXTread.name = "TestXThread name";
testXTread.Start();
this_thread::sleep_for(2ms);
testXTread.Stop();
}
4. lambda临时函数作为线程入口函数
lambda函数基本格式:【lambda】
[捕捉列表](参数)mutable->返回值类型{函数体}
4.1 lambda临时函数
#include<iostream>
using namespace std;
#include<thread>
int main()
{
thread th(
[](int i) {cout << "test lmbda " << i << endl; },
123//传入参数i
);
th.join();
return 0;
}
4.2 类成员中lambda临时函数
#include<iostream>
using namespace std;
#include<thread>
class TestLambda
{
public:
void Start()
{
//重新定义的一个函数,不属于类成员函数,无法访问类成员
/*thread th(
[]() {cout << "TestLambda " << name << endl; }
);
thread th(
[]() {cout << "TestLambda " << this.name << endl; }
);*/
//将this指针传入
thread th(
[this]() {cout << "TestLambda " << name << endl; }
);
th.join();
}
string name = "test lambda";
};
int main()
{
TestLambda tl;
tl.Start();
return 0;
}
5. call_once 多线程调用函数,但函数只进入一次(一般多用于项目初始化部分)
#include<iostream>
using namespace std;
#include<thread>
#include<mutex>
void SyatemInit()
{
cout << "SyatemInit()" << endl;
}
int main()
{
SyatemInit();
SyatemInit();
for (int i=0;i<3;i++)
{
thread th(SyatemInit);
th.detach();
}
getchar();
return 0;
}
#include<iostream>
using namespace std;
#include<thread>
#include<mutex>
void SyatemInit()
{
cout << "SyatemInit()" << endl;
}
void SyatemInitOne()
{
//要定义为静态变量
static std::once_flag flag;//通过该标志确定函数SyatemInit()只调用一次
std::call_once(flag, SyatemInit);
}
int main()
{
SyatemInitOne();
SyatemInitOne();
for (int i=0;i<3;i++)
{
thread th(SyatemInitOne);
th.detach();
}
getchar();
return 0;
}
【二、多线程通信和同步】
1. 多线程状态
- 初始化(init):线程正在被创建
- 就绪(Ready):该线程在就绪列表中,等待CPU调度
- 运行(Running):线程正在运行
- 阻塞(Blocked):该线程被阻塞挂起【pend(锁、时间、信号量等阻塞)、suspend(主动pend)、delay(延时阻塞)、pendtime(因为锁、事件、信号量时间等超时等待)】
- 退出(Exit):该线程运行结束,等待父线程回收其控制块资源。
2. 互斥锁mutex
- lock()与try_lock()
#include<iostream>
using namespace std;
#include<mutex>
#include<thread>
static mutex mux;
void TestThread()
{
//获得锁资源,如果没有则线程阻赛等待,但锁资源会释放掉
mux.lock();
cout << "====================" << endl;
cout << "test 01" << endl;
cout << "test 02" << endl;
cout << "test 03" << endl;
cout << "====================" << endl;
mux.unlock();
}
int main()
{
for (int i = 0; i < 3; i++)
{
thread th(TestThread);
th.detach();
}
getchar();
return 0;
}
#include<iostream>
using namespace std;
#include<mutex>
#include<thread>
static mutex mux;
void TestThread()
{
for (;;)
{
//能监控到获取锁的过程
if (!mux.try_lock())//try_lock有资源开销,一致尝试会造成资源的浪费,控制锁的本身,带来性能开销
{
cout << "." << endl;
this_thread::sleep_for(1ms);//休眠是为了让资源释放掉
continue;
}
cout << "====================" << endl;
cout << "test 01" << endl;
cout << "test 02" << endl;
cout << "test 03" << endl;
cout << "====================" << endl;
mux.unlock();
}
}
int main()
{
for (int i = 0; i < 3; i++)
{
thread th(TestThread);
th.detach();
}
getchar();
return 0;
}
- 互斥锁线程抢占不到资源
#include<iostream>
using namespace std;
#include<mutex>
#include<thread>
void ThreadMainMux(int i)
{
for (;;)
{
mux.lock();
cout << i << "[in]" << endl;
this_thread::sleep_for(1000ms);
mux.unlock();
//释放锁后,操作系统还未清理资源,线程又去申请拿到资源,应该给操作系统时间去释放
//this_thread::sleep_for(1ms);
}
}
int main()
{
for (int i = 0; i < 3; i++)
{
thread th(ThreadMainMux,i+1);
th.detach();
}
getchar();
return 0;
}
释放锁后,休眠1ms,则结果如下:
3. 超时锁 timed_mutex
time_mutex 记录锁的获取情况,多次超时可以记录日志,获取错误情况
#include<iostream>
using namespace std;
#include<mutex>
#include<thread>
timed_mutex tmux;
void ThreadMainTime(int i)
{
for (;;)
{
//try_lock_for==try_lock+sleep
if (!tmux.try_lock_for(chrono::milliseconds(500)))
{
cout << i << "[try_lock_for timeout]" << endl;
continue;
}
cout << i << "[in]" << endl;
this_thread::sleep_for(2000ms);
tmux.unlock();
this_thread::sleep_for(1ms);
}
}
int main()
{
for (int i = 0; i < 3; i++)
{
thread th(ThreadMainTime, i + 1);
th.detach();
}
getchar();
return 0;
}
4. 递归锁recursive_mutex
递归锁在同一个线程中的同一把锁可以锁多次,避免一些不必要的死锁
#include<iostream>
using namespace std;
#include<mutex>
#include<thread>
recursive_mutex rmux;
void task1()
{
rmux.lock();
cout << "task 1()" << endl;
rmux.unlock();
}
void task2()
{
rmux.lock();
cout << "task 2()" << endl;
rmux.unlock();
}
void ThreadMainRec(int i)
{
for (;;)
{
rmux.lock();
task1(); //若是普通锁,就要先解锁,再重入
cout << i << "[in]" << endl;
this_thread::sleep_for(2000ms);
task2();
rmux.unlock();
this_thread::sleep_for(1ms);
}
}
int main()
{
for (int i = 0; i < 3; i++)
{
thread th(ThreadMainRec, i + 1);
th.detach();
}
getchar();
return 0;
}
5. 共享锁shared_mutex
C++14共享超市互斥锁shared_timed_mutex
C++17共享互斥shared_mutex
共享锁包含共享锁和互斥锁
#include<iostream>
using namespace std;
#include<thread>
#include<shared_mutex>
shared_mutex stmux_;
shared_timed_mutex stmux;
//有一个互斥锁在写 无法进行读和写
//有一个线程在读,互斥锁写也被阻塞等待
void ReadThread(int i) {
for (;;) {
stmux.lock_shared();//共享锁 多线程可以一起读
cout << "read" << i << endl;
this_thread::sleep_for(500ms);
stmux.unlock_shared();
this_thread::sleep_for(1ms);
}
}
void WriteThread(int i) {
for (;;) {
stmux.lock_shared();
stmux.unlock_shared();
stmux.lock();//互斥锁,写入
cout << "Write" << i << endl;
this_thread::sleep_for(500ms);
stmux.unlock();
this_thread::sleep_for(1ms);
}
}
int main() {
for (int i = 0; i < 3; i++) {
thread(WriteThread, i).detach();
}
for (int i = 0; i < 3; i++) {
thread(ReadThread, i).detach();
}
getchar();
return 0;
}
【三、锁资源管理和条件变量】
1. 什么是RAII
RAII(Resource Acquisition Is Initialization)是由c++之父Bjarne Stroustrup提出的,为资源获取即初始化。使用局部对象来管理资源的技术称为资源获取即初始化;这里的资源主要是指操作系统中有限的东西如内存、网络套接字等等,局部对象是指存储在栈的对象,它的生命周期是由操作系统来管理的,无需人工介入。
2.手动实现RAII管理mutex资源
#include<iostream>
using namespace std;
#include<mutex>
static mutex mux;
class RAIIMutex {
public:
RAIIMutex(mutex& mux) : mutex_(mux) {//引用必须初始化
mutex_.lock();
cout << "RAIIMutex()->mutex_.lock()" << endl;
}
~RAIIMutex() {
mutex_.unlock();
cout << "~RAIIMutex()->mutex_.unlock()" << endl;
}
private:
mutex& mutex_;
};
void TestMux(int status) {
RAIIMutex lock(mux);
if (status == 1) {
cout << "==1" << endl;
} else {
cout << "!=1" << endl;
}
}
int main() {
TestMux(1);
TestMux(2);
getchar();
return 0;
}
3.lock_guard
std::lock_guard类的构造函数禁用拷贝构造,且禁用移动构造。std::lock_guard类除了构造函数和析构函数外没有其它成员函数。
通过{}控制锁的临界区。
#include "iostream"
using namespace std;
#include<mutex>
#include<thread>
static mutex mutex_;
//void TestLockguard(int i) {//会崩溃,在lock_guard作用区中再次lock会产生崩溃
// lock_guard<mutex> lock(mutex_);
// for (;;) {
// lock_guard<mutex> lock(mutex_);
// cout << i << endl;
// this_thread::sleep_for(500ms);
// }
//}
void TestLockguard(int i) {//会崩溃,在lock_guard作用区中再次lock会产生崩溃
lock_guard<mutex> lock(mutex_, adopt_lock);
for (;;) {
lock_guard<mutex> lock(mutex_);
cout << i << endl;
this_thread::sleep_for(500ms);
}
}
int main() {
for (int i = 0; i < 3; i++) {
thread(TestLockguard, i + 1).detach();
}
getchar();
return 0;
}
3.unique_guard
unlock 可临时解锁控制超时的互斥体;
#include "iostream"
using namespace std;
#include<mutex>
int main() {
static mutex mux_;
{
{
//拥有锁并加锁
unique_lock<mutex> mux(mux_);
mux.unlock();
mux.lock();
}
{
mux_.lock();
//拥有锁 出栈区会自动解锁
unique_lock<mutex> mux(mux_, adopt_lock);
}
{
unique_lock<mutex> mux(mux_, defer_lock);//不拥有 退出不解锁
//加锁 退出解锁
mux.lock();
}
{
//尝试加锁 不阻塞 失败不拥有锁
unique_lock<mutex> mux(mux_, try_to_lock);
if (mux.owns_lock()) {
cout << "owns_lock" << endl;
} else {
cout << "not owns_lock" << endl;
}
}
}
return 0;
}
4.shared_guard
#include "iostream"
using namespace std;
#include<shared_mutex>
int main() {
static shared_timed_mutex mux_;
{
{
//读共享锁
shared_lock<shared_timed_mutex> mux(mux_);
cout << "read" << endl;
}
{
//已经获得读模式锁或者写模式锁的线程再次调用lock的话,行为是未定义的。
//注意:通常不直接使用std::shared_mutex::lock(),而是通过unique_lock或者lock_guard进行管理。
//写互斥锁 使用unique_lock防止死锁
unique_lock<shared_timed_mutex> mux(mux_);
cout << "write" << endl;
}
}
return 0;
}
5.scoped_lock
用于多个互斥体的免死锁RAII封装器
#include"iostream"
using namespace std;
#include<mutex>
#include<thread>
mutex mux1, mux2;
void TestFun1() {
//lock_guard<mutex> lock1(mux1);
scoped_lock lock(mux1, mux2);
cout << "TestFun1 owns lock1(mux1)" << endl;
this_thread::sleep_for(400ms);
//lock_guard<mutex> lock2(mux2);
cout << "TestFun1 owns lock2(mux2)" << endl;
this_thread::sleep_for(500ms);
}
void TestFun2() {
//lock_guard<mutex> lock1(mux2);
scoped_lock lock(mux1, mux2);//scoped_lock 成功获取两个锁 (不会只获取一个锁 避免死锁问题)
cout << "TestFun2 owns lock1(mux2)" << endl;
this_thread::sleep_for(500ms);
//lock_guard<mutex> lock2(mux1);
cout << "TestFun2 owns lock2(mux1)" << endl;
}
int main() {
thread(TestFun1).detach();
thread(TestFun2).detach();
getchar();
return 0;
}
6. 使用互斥锁实现线程通信
主线程发送消息,子线程读取处理消息
sthread.h
#pragma once
#include"xthread.h"
#include<list>
#include<string>
#include<mutex>
#include<iostream>
class SThread : public XThread {
public:
void MessageS(int i);
private:
void Main() override;
std::list<int> list_;
std::mutex mutex_;
};
sthread.cpp
#include "sthread.h"
using namespace this_thread;
void SThread::MessageS(int i) {
std::unique_lock<std::mutex> lock(mutex_);
list_.push_back(i);
std::cout << "push_back i" << i << std::endl;
}
void SThread::Main() {
while (!is_exit()) {
sleep_for(10ms);//增加睡眠时间是为了防止list一直为NULL的时候线程加锁释放一直消耗CPU资源
std::unique_lock<std::mutex> lock(mutex_);
if (list_.empty()) {
continue;
}
while (!list_.empty()) {
std::cout << "front i" << list_.front() << std::endl;
list_.pop_front();
}
}
}
xthread.h
#pragma once
#include <thread>
class XThread {
public:
//设置线程开始标志 启动子线程
virtual void Start();
//设置线程退出标志 线程退出
virtual void Stop();
//等待所有线程完成 会阻塞
virtual void Wait();
bool is_exit();
private:
//处理消息的线程
virtual void Main() = 0;
bool is_exit_ = false;
std::thread th_;
};
xthread.cpp
#include "xthread.h"
void XThread::Start() {
is_exit_ = false;
th_ = std::thread(&XThread::Main, this);//XThread::Main为纯虚函数,成员函数需要传递this指针
}
void XThread::Stop() {
is_exit_ = true;
Wait();
}
void XThread::Wait() {
if (th_.joinable()) {
th_.join();
}
}
bool XThread::is_exit() {
return is_exit_;
}
main.cpp
#pragma once
#include"sthread.h"
using namespace std;
int main() {
SThread* sthread_ = new SThread();
sthread_->Start();
for (int i = 0; i < 10; i++) {
sthread_->MessageS(i + 1);
this_thread::sleep_for(500ms);
}
sthread_->Stop();
getchar();
return 0;
}
7. 条件变量的使用
- condition_variable只能使用unique_lock来锁定线程,因为在condition_variable的内部将通过调用unique_lock的lock()函数来获取mutex,而诸如lock_guard对象并不提供lock()操作。
- 条件变量不支持拷贝和移动操作。
- wait(lck)用于等待通知。而wait(lck,pred)会先判断pred条件,再决定是否阻塞等待,当pred为false则继续等待,否则直接返回,相当于 while(!pred())wait(lck)。
- notify_one()用于通知一个等待线程,而notify_all()用于通知所有等待线程。
#include<iostream>
#include<condition_variable>
#include<list>
#include<mutex>
#include<thread>
using namespace std;
std::mutex mux;
std::list<int> myList;
condition_variable cv;
//写入
void TestCon1() {
for (int i = 0;; i++) {
std::unique_lock<std::mutex> lock(mux);
myList.push_back(i);
cout << "myList.push_back()= " << i << endl;
//通知前,手动解锁以防正在等待的线程被唤醒后又立即被阻塞
//被通知的线程会立即再次阻塞,等待通知线程释放锁
lock.unlock();
this_thread::sleep_for(3s);
cv.notify_one();
}
}
//读取 wait()没有lambda表达式
//void TestCon2() {
// for (;;) {
// std::unique_lock<std::mutex> lock(mux);
// cv.wait(lock);
// while (!myList.empty()) {
// cout << "myList.front()= " << myList.front() << endl;
// myList.pop_front();
// }
// }
//}
//读取 wait()
void TestCon2(int i) {
for (;;) {
std::unique_lock<std::mutex> lock(mux);
cv.wait(lock, [i] {
cout << "in PID" << i << endl;
return !myList.empty();
});
while (!myList.empty()) {
cout << "myList.front()= " << myList.front() << endl;
myList.pop_front();
}
}
}
int main() {
thread th(TestCon1);
th.detach();
for (int i = 0; i < 3; i++) {
thread th2(TestCon2, i + 1);
th2.detach();
}
getchar();
return 0;
}