C++11多线程部分知识点 + 多线程顺序执行 + 交替打印字符串

  • 之前在Linux下操作C的API pthread相关函数 写多线程,或者使用Qt的QThread实现多线程编程,不太具备通用性
  • C++11相较于C++98添加了蛮多的多线程编程的特性,本文记录下其中部分知识。

1. 基础知识

C++ 多线程编程入门基础: 学习链接
C++并发编程 - 同步并发操作:添加链接描述

2. 按序打印

代码解法类似 1.基础知识 里面的最后一个案例
在这里插入图片描述

class Foo {
public:
    Foo() {
    }
    void first(function<void()> printFirst) {
        
        // printFirst() outputs "first". Do not change or remove this line.
        printFirst();
        flag1 = true;
        cv.notify_all();
    }

    void second(function<void()> printSecond) {
        unique_lock<mutex> loc(m);
        while(flag1==false){
            cv.wait(loc);
        }
        // printSecond() outputs "second". Do not change or remove this line.
        printSecond();
        flag2 = true;
        cv.notify_all();
    }

    void third(function<void()> printThird) {
        unique_lock<mutex> loc(m);
        while(flag2==false){
            cv.wait(loc);
        }
        // printThird() outputs "third". Do not change or remove this line.
        printThird();
    }
private:
    mutex m;
    condition_variable cv;
    bool flag1 = false;
    bool flag2 = false;
};

3.交替打印FooBar

在这里插入图片描述在这里插入图片描述

condiation_variable的wait方法有两种:有条件的等待和无条件的等待
wait有条件等待与无条件等待
在这里插入图片描述

class FooBar {
private:
    int n;
    mutex mtx;
    condition_variable cv;
    bool foo_done=false;
public:
    FooBar(int n) {
        this->n = n;
    }
    void foo(function<void()> printFoo) {
        for (int i = 0; i < n; i++) {
            unique_lock<mutex> locker(mtx);
            while(foo_done==true)
                cv.wait(locker);
            // cv.wait(locker,[&](){return foo_done==false;})
            printFoo();
            foo_done=true;
            cv.notify_one();
        }
    }
    void bar(function<void()> printBar) {
        for (int i = 0; i < n; i++) {
            unique_lock<mutex>locker(mtx);
            while(foo_done==false)
                cv.wait(locker);
            //cv.wait(locker,[&](){return foo_done==true;})  除了while还可以采用条件wait
            printBar();
            foo_done=false;
            cv.notify_one();
        }
    }

4. 汇总


/*************************
example1 - join :
	main是主线程,t1是主线程中的子线程,在这个线程里执行函数fun1()。
	使用join()函数,那么主线程就会一直阻塞在t1.join();的位置,直到子线程t1运行结束后,再接着执行后面的语句。

	fun1()函数里写了一个休眠1s的语句,所以t1线程在还没有打印fun1的时候,主线程由于没有写join()函数阻塞起来,就会继续往后执行,先打印出main,等t1休眠了1s后,才打印出fun1。
	join()函数除了阻塞作用,另一个作用就是回收该线程使用到的资源,上面的程序把join()删掉了,子线程t1的资源不能正确回收,程序运行会报错“abort() has been called”。
**************************/
#if 0



#include<iostream>
#include <thread>
#include <chrono>

using namespace std;

void ff() {
	cout << "func1" << endl;
	this_thread::sleep_for(chrono::seconds(1));
}


int main()
{
	thread t1(ff);
	//t1.join();
	cout << "main" << endl;
	return 0;
}

#endif

/*************************
example2 - detach :
	 那如果子线程t1的运行时间太长了,我不想让主线程阻塞等待t1运行,怎么办呢?下面就介绍detach()函数。
	 detach()函数的作用是将子线程和主线程分离,让子线程在后台独立运行。如果主线程运行很快,先结束运行了,子线程还未执行完的话,那么子线程会在后台继续执行,执行完毕时由线程库回收其资源。
	 请看如下例子,主线程中创建子线程t1,通过detach()函数,t1从主线程中分离并开始运行,输出fun1()开始 ,然后休眠1s,这个过程中主线程输出main(),然后子线程休眠结束,输出fun1()结束。
**************************/

#if 0
#include<iostream>
#include <thread>
#include <chrono>

using namespace std;

void fun1() {
	cout << "fun1()开始" << endl;
	this_thread::sleep_for(chrono::seconds(1));  // 休眠1秒
	cout << "fun1()结束" << endl;
}

int main() {
	thread t1(fun1);    // 创建一个线程,名为t1,这个线程会执行fun1()函数
	t1.detach();
	this_thread::sleep_for(chrono::milliseconds(500));  // 休眠0.5秒
	cout << "main()" << endl;
	system("pause");
}


#endif



/*************************
example3 - 互斥锁mutex :
	- lock_guard   没有lock和unlock接口,靠析构函数自动释放,作用域是局部空间 粒度较大
	- unique_lock  有lock和unlock的接口,可以锁定代码段,粒度更小

**************************/

#if 0

#include<iostream>
#include <thread>
#include <mutex>
#include <chrono>

using namespace std;

mutex m;

void fun1() {
	lock_guard<mutex>  loc(m);	// 构造函数里自动加锁
	cout << "fun1" << endl;     // 做别的事情
	// lock_guard出了这个作用域,自动解锁
}

void fun2() {
	unique_lock<mutex>  loc(m);	// 构造函数里自动加锁
	cout << "do A" << endl;
	loc.unlock();			    // 解锁

	loc.lock();		            // 重新加锁
	cout << "do B" << endl;
	loc.unlock();				// 解锁
}

int main() {
	thread t1(fun2);    // 创建一个线程,名为t1,这个线程会执行fun1()函数
	t1.detach();
	this_thread::sleep_for(chrono::milliseconds(500));  // 休眠0.5秒
	cout << "main()" << endl;
	system("pause");
}


#endif

/*************************

example4 - 条件变量condition_variable :
	 上述的unique_lock通常是和condition_variable配合使用的。当 condition_variable 对象的 wait() 函数被调用的时候,它使用 unique_lock来锁住当前线程。
	 当前线程会一直被阻塞,直到另外一个线程在相同的 condition_variable 对象上调用了notification()函数来唤醒当前线程。

**************************/

#if 0

/*
//原始版本  线程顺序容易出错

#include<iostream>
#include <thread>
#include <mutex>
#include <chrono>

using namespace std;

mutex m;

class Foo {
public:
	Foo() {}

	void first() {
		for (int i = 0; i < 10; ++i) {
			cout << "first" << endl;	// 打印10个first
		}
	}

	void second() {
		for (int i = 0; i < 10; ++i) {
			cout << "second" << endl;  // 打印10个second
		}
	}

	void third() {
		for (int i = 0; i < 10; ++i) {
			cout << "third" << endl;  // 打印10个third
		}
	}
};

int main() {
	Foo F;
	// 创建3个线程,分别调用first()、second()、third()函数打印
	thread(&Foo::first, &F).detach();
	thread(&Foo::second, &F).detach();
	thread(&Foo::third, &F).detach();
	system("pause");
}

*/


// 如何让三个线程按序打印,也就是不管哪个线程先被创建,都按照first()、second()、third()的顺序打印呢?

/*
 这时候就可以用前面提到过的unique_lock和condition_variable。我们设置两个标志位,flag1为true表示允许second打印、flag2为true表示允许third打印,先初始化flag1、flag2为false。线程创建后,可能出现以下的执行顺序:
(1)如果在first()还未打印完时(或者压根还没开始执行时),执行second(),由于此时flag1被初始化为false,进入while循环,调用条件变量的wait函数,会将当前线程阻塞起来。此时需要等另一个子线程执行first()打印完毕后,将flag1设为true,然后调用notify_all()将阻塞的线程唤醒,second()才能接着执行,完成打印。
(2)如果first()已经执行完毕,然后执行second(),那么flag1为true,执行second()的那个线程不会进入while循环,也不会被阻塞起来,能顺利执行。
third()的执行先后也可参考以上两种情况。
*/


#include<iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>

using namespace std;


class Foo {
private:
	mutex m;                      // 互斥锁
	condition_variable cd_v;      // 条件变量
	bool flag1;                   // 两个标志位
	bool flag2;
public:
	Foo() : flag1(false), flag2(false) {}   // 初始化两个标志位为false,表示second和third还不能打印

	void first() {
		for (int i = 0; i < 10; ++i) {
			cout << "first" << endl;
		}
		flag1 = true;        // first执行后,将flag1置为true,表示second可以打印了
		cd_v.notify_all();   // 唤醒等待队列里的所有线程
	}

	void second() {
		unique_lock<mutex> loc(m);	// 加锁
		// (1)先判断flag1是否为true,true则不会进入while循环,顺利向下执行;
		// (2)flag1为false则执行wait函数,释放锁,并进入对象cd_v的等待队列;
		// (3)当别的线程调用对象cd_v的notify_all()函数,会唤醒在等待队列中的这个线程,然后重新获得锁,继续步骤(1)
		while (flag1 == false) {
			cd_v.wait(loc);
		}
		for (int i = 0; i < 10; ++i) {
			cout << "second" << endl;
		}
		flag2 = true;      // 修改flag2表示third已经可以打印了
		cd_v.notify_all(); // 唤醒等待队列里的所有线程
		// 当离开这个局部作用域时,unique_lock会调用析构函数,自动解锁
	}

	void third() {
		unique_lock<mutex> loc(m);
		while (flag2 == false) {
			cd_v.wait(loc);
		}
		for (int i = 0; i < 10; ++i) {
			cout << "third" << endl;
		}
	}
};


int main() {
	Foo F;
	// thread 需要传参,尤其对类对象,函数 + 类对象的地址
	thread t1(&Foo::first, &F);
	thread t2(&Foo::second, &F);
	thread t3(&Foo::third, &F);

	t1.detach();
	t2.detach();
	t3.detach();

	system("pause");
}

#endif // 1



#if 0

//   题目一、交替打印字符串

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <functional>

using namespace std;

class FooBar {
private:
	int n;
	mutex mtx;
	condition_variable cv;
	bool foo_done = false;
public:
	FooBar(int n) {
		this->n = n;
	}
	void foo(function<void()> printFoo) {
		for (int i = 0; i < n; i++) {
			unique_lock<mutex> locker(mtx);
			while (foo_done == true)
				cv.wait(locker);

			printFoo();
			foo_done = true;
			cv.notify_one();
		}
	}
	void bar(function<void()> printBar) {
		for (int i = 0; i < n; i++) {
			unique_lock<mutex>locker(mtx);
			while (foo_done == false)
				cv.wait(locker);
			printBar();
			foo_done = false;
			cv.notify_one();
		}
	}
};


void printBar() {
	cout << "bar" << endl;
}
void printFoo() {
	cout << "Foo" << endl;
}

int main() {
	FooBar F(4);
	// thread 需要传参,尤其对类对象,函数 + 类对象的地址
	thread t1(&FooBar::foo, &F,printBar);
	thread t2(&FooBar::bar,&F,printFoo);

	t1.detach();
	t2.detach();

	system("pause");
}


#endif // 1


#if 1

//   题目一、交替打印字符串

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <functional>

using namespace std;


int n = 5;
mutex mtx;
condition_variable cv;
bool foo_done = false;


void foo(function<void(int)> printFoo) {
	for (int i = 0; i < n; i++) {
		unique_lock<mutex> locker(mtx);
		while (foo_done == true)
			cv.wait(locker);

		printFoo(i);
		foo_done = true;
		cv.notify_one();
	}
}
void bar(function<void(int)> printBar) {
	for (int i = 0; i < n; i++) {
		unique_lock<mutex>locker(mtx);
		while (foo_done == false)
			cv.wait(locker);
		printBar(i);
		foo_done = false;
		cv.notify_one();
	}
}

void printBar(int i) {
	cout << "bar" <<i<< endl;
}
void printFoo(int i) {
	cout << "Foo" <<i<< endl;
}

int main() {
	// thread 需要传参,尤其对类对象,函数 + 类对象的地址
	thread t1(foo, printBar);
	thread t2(bar,printFoo);

	t1.detach();
	t2.detach();

	system("pause");
}


#endif // 1

5. 题目2 题目3 可运行代码


// 题目2 多线程顺序执行
#if 0

#include<iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>

using namespace std;


class Foo {
private:
	mutex m;                      // 互斥锁
	condition_variable cd_v;      // 条件变量
	bool flag1;                   // 两个标志位
	bool flag2;
public:
	Foo() : flag1(false), flag2(false) {}   // 初始化两个标志位为false,表示second和third还不能打印

	void first() {
		for (int i = 0; i < 10; ++i) {
			cout << "first" << endl;
		}
		flag1 = true;        // first执行后,将flag1置为true,表示second可以打印了
		cd_v.notify_all();   // 唤醒等待队列里的所有线程
	}

	void second() {
		unique_lock<mutex> loc(m);	// 加锁
		// (1)先判断flag1是否为true,true则不会进入while循环,顺利向下执行;
		// (2)flag1为false则执行wait函数,释放锁,并进入对象cd_v的等待队列;
		// (3)当别的线程调用对象cd_v的notify_all()函数,会唤醒在等待队列中的这个线程,然后重新获得锁,继续步骤(1)
		while (flag1 == false) {
			cd_v.wait(loc);
		}
		for (int i = 0; i < 10; ++i) {
			cout << "second" << endl;
		}
		flag2 = true;      // 修改flag2表示third已经可以打印了
		cd_v.notify_all(); // 唤醒等待队列里的所有线程
		// 当离开这个局部作用域时,unique_lock会调用析构函数,自动解锁
	}

	void third() {
		unique_lock<mutex> loc(m);
		while (flag2 == false) {
			cd_v.wait(loc);
		}
		for (int i = 0; i < 10; ++i) {
			cout << "third" << endl;
		}
	}
};

int main() {
	Foo F;
	// thread 需要传参,尤其对类对象,函数 + 类对象的地址
	thread t1(&Foo::first, &F);
	thread t2(&Foo::second, &F);
	thread t3(&Foo::third, &F);

	t1.detach();
	t2.detach();
	t3.detach();

	system("pause");
}

#endif


// 题目3  多线程交替打印字符
#if 0



#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <functional>

using namespace std;


int n = 5;
mutex mtx;
condition_variable cv;
bool foo_done = false;

void foo(function<void(int)> printFoo) {
	for (int i = 0; i < n; i++) {
		unique_lock<mutex> locker(mtx);
		while (foo_done == true)
			cv.wait(locker);

		printFoo(i);
		foo_done = true;
		cv.notify_one();
	}
}
void bar(function<void(int)> printBar) {
	for (int i = 0; i < n; i++) {
		unique_lock<mutex>locker(mtx);
		while (foo_done == false)
			cv.wait(locker);
		printBar(i);
		foo_done = false;
		cv.notify_one();
	}
}

void printBar(int i) {
	cout << "bar" <<i<< endl;
}
void printFoo(int i) {
	cout << "Foo" <<i<< endl;
}

int main() {
	// thread 需要传参,尤其对类对象,函数 + 类对象的地址
	thread t1(foo, printBar);
	thread t2(bar,printFoo);

	t1.detach();
	t2.detach();

	system("pause");
}


#endif // 1


6. 题目2题目3 写法2

6.1 三个线程顺序打印


// 三个线程 顺序打印
#if 1
condition_variable cv;
mutex m;
int flag = 0;
int n = 10;

void printA() {
	unique_lock<mutex> loc(m);
	for (int i = 0; i < n; i++){
		cout << "A:" << i << endl;
	}
	flag = 1;
	loc.unlock();
	cv.notify_all();
	
}

void printB() {
	unique_lock<mutex> loc(m);
	while (flag != 1)
		cv.wait(loc);
	for (int i = 0; i < n; i++) {
		cout << "B:" << i << endl;
	}
	flag = 2;
	loc.unlock();
	cv.notify_all();
}

void printC() {
	unique_lock<mutex> loc(m);
	while (flag != 2)
		cv.wait(loc);
	for (int i = 0; i < n; i++) {
		cout << "C:" << i << endl;
	}
	loc.unlock();
	cv.notify_all();
}

int main()
{
	thread t1(printA);
	thread t2(printB);
	thread t3(printC);
	
	t1.detach();
	t2.detach();
	t3.detach();
	system("pause");
	return 0;
}

#endif // 1

6.2 三个线程交替打印


// 三个线程 交替打印
#if 0

condition_variable cv;
mutex m;
int flag = 0;
int n = 10;

void printA() {
	
	for (int i = 0; i < n; i++) {
		unique_lock<mutex> loc(m);
		while (flag != 0) cv.wait(loc);
		cout << "A:" << i << endl;
		flag = 1;
		loc.unlock();
		cv.notify_all();
	}
}

void printB() {

	for (int i = 0; i < n; i++) {
		unique_lock<mutex> loc(m);
		while (flag != 1) cv.wait(loc);
		cout << "B:" << i << endl;
		flag = 2;
		loc.unlock();
		cv.notify_all();
	}
}

void printC() {

	for (int i = 0; i < n; i++) {
		unique_lock<mutex> loc(m);
		while (flag != 2) cv.wait(loc);
		cout << "C:" << i << endl;
		flag = 0;
		loc.unlock();
		cv.notify_all();
	}
}

int main()
{
	thread t1(printA);
	thread t2(printB);
	thread t3(printC);

	t1.detach();
	t2.detach();
	t3.detach();
	system("pause");
	return 0;
}

#endif // 1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ray Song

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值