C++多线程编程学习笔记
推荐的C++多线程编程学习链接: C++11 多线程编程-小白零基础到手撕线程池.
自己按照课程敲的代码段
#include <iostream>
#include <thread>
#include <string>
#include <memory> // 智能指针使用
#include <mutex> // 互斥锁使用
#include <windows.h> // Sleep使用
#include <string>
#include <queue>
#include <condition_variable>
#include <atomic>
#include <future> //future packaged_task async
// 第一节课程
void PrintHelloWord(std::string str)
{
std::cout << str << std::endl;
}
void DetachFunction(std::string str)
{
std::cout << str << std::endl;
}
void PrintBigData()
{
for (long long i = 0; i < 1e1; ++i) {
std::cout << i << std::endl;
}
}
// 第二节课程
void ReferenceFunc(int& RefF)
{
RefF++;
std::cout << "ReferenceFunc RefF value:" << RefF << std::endl;
}
void PtrFunc(int * p) {
std::cout << "*p" << *p << std::endl;
}
class A {
public:
void foo() {
std::cout << "A class execute!" << std::endl;
}
private:
friend void friendFunc(A *p);
void privateFunc() {
std::cout << "A class privateFunc execute!" << std::endl;
}
};
void friendFunc(A* p) {
p->privateFunc();
}
std::mutex mtx;
// 第三节课程 互斥量解决数据共享问题
void AddFuncForMutex(int& a_mtx) {
for (long long i = 0; i < 1e6; i++) {
mtx.lock(); // 不加锁数据不对,枷锁就对了
a_mtx++;
mtx.unlock();
// 上面的上锁和解锁可以改用std::lock_guard<std::mutex> guard(mtx); // 不用管所锁的lock和unlock
}
}
// 死锁:2把锁互相等着对方释放
std::mutex mtx1;
std::mutex mtx2;
void func_1_lock()
{
mtx1.lock();
Sleep(3000);
std::cout << "1" << std::endl;
mtx2.lock();
std::cout << "2" << std::endl;
mtx1.unlock();
std::cout << "3" << std::endl;
mtx2.unlock();
std::cout << "4" << std::endl;
}
void func_2_lock()
{
mtx2.lock();
std::cout << "5" << std::endl;
mtx1.lock();
std::cout << "6" << std::endl;
mtx2.unlock();
std::cout << "7" << std::endl;
mtx1.unlock();
std::cout << "8" << std::endl;
}
void AddFuncForMutexLockGuard(int& a_mtx) {
for (long long i = 0; i < 1e6; i++) {
std::lock_guard<std::mutex> guard(mtx); // 不用管锁的lock和unlock,构造析构解决
a_mtx++;
}
}
void AddFuncForMutexUniqueGuard(int& a_mtx) {
for (long long i = 0; i < 1e6; i++) {
std::unique_lock<std::mutex> guard(mtx); // 不用管锁的lock和unlock
a_mtx++;
}
}
std::timed_mutex t_mtx;
void AddFuncForMutexUniqueGuardTime(int& a_mtx) {
for (long long i = 0; i < 2; i++) {
std::unique_lock<std::timed_mutex> guard(t_mtx, std::defer_lock); // 构造时不上锁
if (guard.try_lock_for(std::chrono::seconds(2))) { // 等待2秒看能否上锁
std::this_thread::sleep_for(std::chrono::seconds(1));
a_mtx++;
}
}
}
// 饿汉模式
static std::once_flag once;
class Log
{
private:
//Log() = default;
static Log* log;
public:
static Log* GetInstance() {
//static Log log1; // 饿汉模式
//return log1;
std::call_once(once, init); // 防止多线程调用多次
return log;
}
void PrintErrLog()
{
std::cout << __TIME__ <<"SingleTon there is error!"<< std::endl;
}
static void init() {
if (log == nullptr) // 懒汉模式线程不安全
{
log = new Log();
std::cout << "Log GetInstance init only execute once" << std::endl;
}
}
};
Log* Log::log = nullptr; // 类中静态成员需要初始化
void PrintSingleTonErrorLog()
{
Log::GetInstance()->PrintErrLog();
}
// 生产者消费者
std::queue<int> g_queue;
std::condition_variable g_cv;
bool stop = false;
void Productor() {
for (int i = 0; i < 5; i++) {
{
std::unique_lock<std::mutex> lock(mtx);
g_queue.push(i);
g_cv.notify_one();// 知会消费者一次
std::cout << "Productor : " << i <<std::endl;
}
std::this_thread::sleep_for(std::chrono::microseconds(100)); // 延迟100毫秒让显示全
}
stop = true;
g_cv.notify_one();// 最后知会消费者一次,把程序退出
}
void Consumer() {
while (1) {
std::unique_lock<std::mutex> lock(mtx);
g_cv.wait(lock, []() {return !g_queue.empty() || stop; }); // 由notify_one知会是否阻塞这里
if (stop) {
break;
}
int value = g_queue.front();
g_queue.pop();
std::cout << "Consumer : " << value << std::endl;
}
}
// 原子变量
std::atomic<int> data(0);
void AtomicFunc() {
for (int i = 0; i < 1000; i++) {
data++; // 原子变量自带锁,且效率更高
}
}
// async future packaged_task promise
int AsyncFunc() {
int i;
for (i = 0; i < 1000; i++) {
data++; // 原子变量自带锁,且效率更高
}
return i;
}
void PromiseFunc(std::promise<int> &f) {
f.set_value(100);
}
int main()
{
std::cout<<"course one start"<<std::endl;
std::thread thread1(PrintHelloWord, "hello hello");
thread1.join(); // 这条语句保证只有当子线程thread1运行完毕,主线程才会接着往下
// 当一个线程调用了join函数时,它会被阻塞,直到被调用join函数的线程执行结束
// 主线程回收子线程资源
std::thread thread2(DetachFunction, "DetachFunction");
thread2.detach(); // 主线程可以先于子线程结束,主线程和子线程分离,子线程的资源由其他线程释放
std::thread thread3(PrintBigData);
if (thread3.joinable()) { // 确定可以用join时再用
thread3.join();
}
std::cout << "over!" << std::endl; //只有当thread3中的数打印完以后,主线程才往下执行
std::cout << "course one end" << std::endl;
std::cout << "course two start" << std::endl;
int RefF = 1;
std::thread thread4(ReferenceFunc, std::ref(RefF)); // 引用变量传递的使用,得用ref()函数
thread4.join();
std::cout << "RefF: " << RefF << std::endl;
int* ptr = new int(1);
std::thread thread5(PtrFunc, ptr);
delete ptr; //因为主线陈已经主动释放指针了,所以子线程thread5中打印为随机值,不为1
thread5.join();
// 线程在调用类对象时,最好用智能指针,解决智能指针的释放问题
std::shared_ptr<A> a = std::make_shared<A>(); // 这里只能用shared,不能用ubique
//std::unique_ptr<A> b = std::make_unique<A>();
std::thread thread6(&A::foo, a); // 第一个参数传的是类函数的地址,第二个为类对象的地址,即指针
A b;
std::thread thread7(friendFunc, &b); // 第一个参数传的是友元函数,第二个为类对象的地址,即指针
std::cout << "course two end" << std::endl;
std::cout << "course three start 互斥量解决数据共享问题" << std::endl;
// 1.避免数据竞争
int a_mtx = 0;
std::thread thread8(AddFuncForMutex, std::ref(a_mtx));
std::thread thread9(AddFuncForMutex, std::ref(a_mtx));
thread8.join(); // 这里不把thread8.join()写在定义thread8后面,就是想让thread8和thread9数据竞争
thread9.join();
std::cout << "数据竞争:a_mtx value shoud be 2e6? actual a_mtx:" << a_mtx << std::endl;
// 2.死锁
//std::thread thread10(func_1_lock);
//std::thread thread11(func_2_lock); // 打印只有5/n1,死锁了
死锁的解决方法:对于每个子线程,都先获取mtx1锁,再获取mtx2锁
//thread10.join();
//thread11.join();
std::cout << "死锁结束" << std::endl;
// 3.std::lock_guard 互斥量封装类,用于保护共享数据,防止数据竞争,只能在局部作用域中使用
a_mtx = 0;
std::thread thread12(AddFuncForMutexLockGuard, std::ref(a_mtx));
std::thread thread13(AddFuncForMutexLockGuard, std::ref(a_mtx));
thread12.join();
thread13.join();
std::cout << "lock_guard:a_mtx value shoud be 2e6? actual a_mtx:" << a_mtx << std::endl;
// 4.C++11 std::unique_lock 更加灵活的处理,延迟枷锁、条件变量、超时,同时占的资源也会增多
// 4.1 有lock_guard功能
a_mtx = 0;
std::thread thread14(AddFuncForMutexUniqueGuard, std::ref(a_mtx));
std::thread thread15(AddFuncForMutexUniqueGuard, std::ref(a_mtx));
thread14.join();
thread15.join();
std::cout << "unique_lock:a_mtx value shoud be 2e6? actual a_mtx:" << a_mtx << std::endl;
// 4.2 等待一段时间再上锁,如果时间超时直接返回
a_mtx = 0;
std::thread thread16(AddFuncForMutexUniqueGuardTime, std::ref(a_mtx));
std::thread thread17(AddFuncForMutexUniqueGuardTime, std::ref(a_mtx));
thread16.join();
thread17.join();
std::cout << "unique_lock:a_mtx value shoud be 3? actual a_mtx:" << a_mtx << std::endl; // 卡伊画时序图看看为啥是3,有一次冲掉了
// 5.单例,防止new多份,除了call_once,在GetInstance();里面用lock_guard<mutex> lock(mtx);也是可以解决的
std::thread thread18(PrintSingleTonErrorLog);
std::thread thread19(PrintSingleTonErrorLog);
thread18.join();
thread19.join();
// 6.condition_variable使用场景 生产者-消费者模型
std::thread thread20(Productor);
std::thread thread21(Consumer);
thread20.join();
thread21.join();
// 7.原子变量自带锁且效率更高
std::thread thread22(AtomicFunc);
std::thread thread23(AtomicFunc);
thread22.join();
thread23.join();
std::cout << "data:" << data << std::endl;
data.store(1); // 给data赋值1,保证操作是原子的,线程安全的
std::cout << "data:" << data.load() << std::endl; //.load() 也可以输出
//8. 异步
//8.1
std::future<int> future_reslt = std::async(std::launch::async, AsyncFunc); // 这句表示AsyncFunc已经在后台执行了,已经是另外一个线程了
std::cout << "AsyncFunc: " << AsyncFunc() << std::endl; // AsyncFunc函数里面没有共享的数据,所以没有数据竞争,不用考虑锁
std::cout << "future_reslt.get(): " << future_reslt.get() << std::endl;
//8.2
std::packaged_task<int()> task(AsyncFunc); // 封装一下
auto future_reslt1 = task.get_future();
std::thread thread24(std::move(task)); // packaged_task是一个可移动对象,放到Thread中执行需要转义
thread24.join();
std::cout << "AsyncFunc: " << AsyncFunc() << std::endl; // AsyncFunc函数里面没有共享的数据,所以没有数据竞争,不用考虑锁
std::cout << "future_reslt1.get(): " << future_reslt1.get() << std::endl; // get()会阻塞当前线程,直到获取
//8.3 promise 用于在一个线程中产生值,另外一个线程去使用,与future搭配使用
std::promise<int> f;
auto future_result2 = f.get_future();
std::thread thread25(PromiseFunc, std::ref(f));
thread25.join();
std::cout << "future_result2.get(): " << future_result2.get() << std::endl;
std::cout << "course three end 互斥量解决数据共享问题" << std::endl;
system("pause");
return 0;
}
执行结果:
course one start
hello hello
DetachFunction
0
1
2
3
4
5
6
7
8
9
over!
course one end
course two start
ReferenceFunc RefF value:2
RefF: 2
*p-572662307
course two end
A class privateFunc execute!
A class execute!
course three start 互斥量解决数据共享问题
数据竞争:a_mtx value shoud be 2e6? actual a_mtx:2000000
死锁结束
lock_guard:a_mtx value shoud be 2e6? actual a_mtx:2000000
unique_lock:a_mtx value shoud be 2e6? actual a_mtx:2000000
unique_lock:a_mtx value shoud be 3? actual a_mtx:4
Log GetInstance init only execute once
21:30:41SingleTon there is error!
21:30:41SingleTon there is error!
Productor : 0
Consumer : 0
Productor : 1
Consumer : 1
Productor : 2
Consumer : 2
Productor : 3
Consumer : 3
Productor : 4
Consumer : 4
data:2000
data:1
AsyncFunc: 1000
future_reslt.get(): 1000
AsyncFunc: 1000
future_reslt1.get(): 1000
future_result2.get(): 100
course three end 互斥量解决数据共享问题
请按任意键继续. . .