C++多线程笔记

#include <iostream>
#include <thread>
#include <memory>
#include <functional>
#include <mutex>
#include <list>
#include <future>
#include <atomic>
#include <chrono>

// 线程创建
namespace Thread {
	class A {
	public:
		A(int a) : ma(a) {
			std::cout << "A::A():" << std::this_thread::get_id() << std::endl;
		}
		A(const A& a) : ma(a.ma) {
			std::cout << "A(const A&): " << std::this_thread::get_id() << std::endl;
		}

		//~A() { std::cout << "~A::A()" << std::endl; }

		void operator()() {
			std::cout << "A::()" << std::endl;
		}

		void func(int) {}

		int ma = 10;
	};

	void func()
	{
		std::cout << " start running...." << std::endl;
		// ....
		std::cout << " end running...." << std::endl;
	}

	void func1(const A& a, char* ptr, std::unique_ptr<int> uq)
	{
		// 修改对象a不会影响主线程的0

		// a虽然是引用,但实际传的的拷贝;
		// 指针在detach子线程时,绝对有问题
		//std::cout << "func1 -> i   = " << a.ma << std::endl; 
		//std::cout << "func1 -> ptr = " << ptr  << std::endl;
		std::cout << " sub thread id : " << std::this_thread::get_id() << std::endl; // 线程ID
	}

	void func2(const std::unique_ptr<int>& uq)
	{
		std::cout << *uq.get() << std::endl;
	}

	void test() {
		// std::cout << "main thread id : " << std::this_thread::get_id() << std::endl;

		// 1、普通函数创建线程
		// std::thread m_thread(func);
		// A a(1);
		// char ptr[] = "hello world";
		// int i = 11;
		// std::thread m_thread(func1, a, ptr);
		// std::thread m_thread(func1, i, ptr);      // detach: 传i不安全,函数的参数a在子线程构建
		// std::thread m_thread(func1, A(i), ptr); // detach: 传临时对象,安全, A(i) 和 函数的参数a在主线程构建
		// std::thread m_thread(func1, std::ref(a), ptr); // std::ref传递引用,对象a不会调用拷贝构造

		// 传递智能指针
		// std::unique_ptr<int> uq1(new int(1));
		// std::unique_ptr<int> uq2 = std::make_unique<int>(1);
		// std::unique_ptr<int> uq3 = std::move(uq1); // uq1为空
		// std::thread m_thread(func2, std::move(uq1));

		// 类的成员函数
		// A a(1);
		// std::thread m_thread(&A::func, a, 1); // 类A调用拷贝构造函数
		// std::thread m_thread(&A::func, &a, 1); // 类A没有调用拷贝构造函数, &a 等价于 std::ref(a)

		// 2、类对象创建线程
		// A a;
		// std::thread m_thread(a); // 对象a会被拷贝              

		// 3、用lambda函数创建线程
		// auto lambda_func = []() {
		// 	std::cout << "lambda func" << std::endl;
		// };

		// std::thread m_thread(lambda_func);
		// std::thread m_thread([]() {std::cout << "lambda func" << std::endl; });

		// m_thread.join();       // 主线程等待子线程执行完毕,才能继续往下执行
		// m_thread.detach();   // 分离,主线程不等待子线程执行完毕,继续往下执行
		// join 和 detach 不能同时使用,否则会异常

		// 判断是否可以成功使用join/detach,true:可以, false:不可以
		//if(m_thread.joinable()) {
		//	std::cout << "joinable == true" << std::endl;
		//} else {
		//	std::cout << "joinable == false" << std::endl;
		//}

		// std::cout << "end...." << std::endl;
	}
}

// 互斥量
namespace Mutex {
	class A {
	public:
		void mvRead() {
			for (int i = 0; i < 10000; ++i) {
				std::cout << "read miValue = " << miValue << std::endl;
			}
		}

		void mvWrite() {
			for (int i = 0; i < 100; ++i) {

				++miValue;

				std::cout << "write miValue = " << miValue << std::endl;
			}
		}

	private:
		int miValue = 0;
	};

	void test() {
		int a = 10;

		// mutext : 互斥量
		{
			std::mutex m_mutex;
			m_mutex.lock();
			a = 10;
			m_mutex.unlock();
		}

		// std::lock_guard
		{
			std::mutex m_mutex;
			//std::lock_guard<std::mutex> m_lock_guard1(m_mutex); // 相当于lock....unlock
			//a = 11;
			
			// std::lock_guard的参数std::adopt_lock表示:互斥量已经被lock,不需要再lock
			m_mutex.lock();
			std::lock_guard<std::mutex> m_lock_guard2(m_mutex, std::adopt_lock);
			a = 11;
		}

		// std::lock:一次锁多个互斥量
		{
			std::mutex m_mutex1;
			std::mutex m_mutex2;
			std::lock(m_mutex1, m_mutex2);
			a = 11;
			m_mutex1.unlock();
			m_mutex2.unlock();
		}

		// std::unique_lock:可以自己设置lock和unclok
		{
			std::mutex m_mutex;
			m_mutex.lock();
			// std::unique_lock<std::mutex> uq_lock1(m_mutex);                       // 构造 并且 clok (如果mutex之前已经被lock,则会出现异常)
			// std::unique_lock<std::mutex> uq_lock2(m_mutex, std::adopt_lock);   // 构造 互斥量已经被lock,不需要再lock,否则lock
			// std::unique_lock<std::mutex> uq_lock2(m_mutex, std::defer_lock);   // 构造 但 不lock
			// std::unique_lock<std::mutex> uq_lock3(m_mutex, std::try_to_lock);  // 构造 尝试lock
			a = 11;
			// uq_lock1.unlock();
			// uq_lock1.lock();
			m_mutex.unlock();
		}
	}
}

// 条件变量
namespace Condition {
	class A {
	public:
		// 接收
		void mInMsg() {
			for (int i = 0; i < 100000; ++i) {
				std::cout << "插入一个元素: " << i << std::endl;

				std::unique_lock<std::mutex> uq_lock(mMutex);
				mlstMsg.push_back(i);

				// notify_one: 唤醒wait,只能通知一个线程
				mConditionVariable.notify_one();

				// notify_all
			}
		}

		// 发送
		void mOutMsg() {
			while(1) {
				std::unique_lock<std::mutex> uq_lock(mMutex);
				// wait:等待:
				//      如果第二个参数的返回值时false,那么wait将解锁互斥量,并堵塞到本行堵塞到其他某个线程调用noitfy_one为止
				//      如果第二个参数的返回值时true,那么wait直接返回
				//      如果wait没有第二个参数,那么就跟返回false一样
				// wait被唤醒后:wait不断的尝试重新获取互斥量锁,如果获取不到锁,那么流程就卡在wait这里等着获取; 如果获取到了锁(等于上锁),那么wait                         
				mConditionVariable.wait(uq_lock, [this, &uq_lock]() {
					return !mlstMsg.empty();
				});

				// 流程走到这,互斥锁一定锁着
				std::cout << "取出一个元素: " << mlstMsg.front() << std::endl;
				mlstMsg.pop_front();
				uq_lock.unlock();
			}
		}

	private:
		std::list<int> mlstMsg;
		std::mutex mMutex;
		std::condition_variable mConditionVariable; // 条件变量
	};

	void test() {
		A a;
		std::thread in_thread(&A::mInMsg, &a);
		std::thread out_thread1(&A::mOutMsg, &a);
		std::thread out_thread2(&A::mOutMsg, &a);

		in_thread.join();
		out_thread1.join();
		out_thread2.join();
	}
}

// 线程返回值
namespace ThreadRetValue {
	int myThread(int) {
		std::cout << "sub start thread id : " << std::this_thread::get_id() << std::endl;

		std::chrono::milliseconds dura(1000); // 1s
		std::this_thread::sleep_for(dura);

		std::cout << "sub end thread id : " << std::this_thread::get_id() << std::endl;

		return 5;
	}

	// std::promise
	void myThread1(std::promise<int>& mPromise, int iValue) {
		//std::cout << "sub start thread id : " << std::this_thread::get_id() << std::endl;

		std::chrono::milliseconds dura(1000); // 1s
		std::this_thread::sleep_for(dura);

		mPromise.set_value(iValue + 100); // 把结果存到promise

		//std::cout << "sub end thread id : " << std::this_thread::get_id() << std::endl;
	}

	void myThread2(std::promise<int>& mPromise) {
		std::cout << "sub start thread id : " << std::this_thread::get_id() << std::endl;

		std::chrono::milliseconds dura(1000); // 1s
		std::this_thread::sleep_for(dura);

		std::future<int> m_future = mPromise.get_future();

		std::cout << "get value : " << m_future.get() << std::endl;

		std::cout << "sub end thread id : " << std::this_thread::get_id() << std::endl;
	}

	class A {
	public:
		int myThread(int) {
			std::cout << "sub start thread id : " << std::this_thread::get_id() << std::endl;

			std::chrono::milliseconds dura(1000); // 1s
			std::this_thread::sleep_for(dura);

			std::cout << "sub end thread id : " << std::this_thread::get_id() << std::endl;

			return 5;
		}
	};

	void test() {
		std::cout << "start main thread id : " << std::this_thread::get_id() << std::endl;

		// std::future
		// std::async
		// std::shared_future
		{
			// std::async:创建一个线程
			// std::launch::deferred:表示线程入口函数调用被延迟到wait或者get函数调用时才执行
			//                       如果wait或者get没有被调用,则线程入口函数不会被调用
			// std::launch::deferred:延迟调用,并且没有创建新线程,是在主线程中调用的线程入口函数
			// std::launch::async:在调用async函数的时候就开始创建线程

			// 普通函数
			// std::future<int> future = std::async(myThread, 1); // 创建一个线程并开始执行

			// 类成员函数
			A a;
			// std::future<int> future = std::async(&A::myThread, &a, 12);
			// std::future<int> future = std::async(std::launch::deferred, &A::myThread, &a, 12);
			std::future<int> future = std::async(std::launch::async, &A::myThread, &a, 12);

			std::cout << "continue....." << std::endl;

			// 等待1s
			std::future_status status = future.wait_for(std::chrono::seconds(1));
			
			// 等待超时
			if (status == std::future_status::timeout) {}
			// 成功返回
			else if (status == std::future_status::ready) {}
			// 延迟返回
			else if (status == std::future_status::deferred) {}

			// 等待线程返回,本身并不返回结果
			// ret.wait();

			// get: 阻塞在这,等待子线程执行完毕,拿到结果,只能get一次,主要是get函数的设计是个移动语义
			// std::cout << "return value : " << future.get() << std::endl;
			// std::cout << "return value : " << ret.get() << std::endl; ///程序运行异常

            // future.valid(); // future是否有效
			
			// std::shared_future: get():复制数据
			std::shared_future<int> sh_future1(std::move(future)); // future就空了
			// std::shared_future<int> sh_future2(future.share());
			
			std::cout << "return value : " << sh_future1.get() << std::endl;
			std::cout << "return value : " << sh_future1.get() << std::endl;
		}

		// std::package_task:打包任务。把各种可调用对象包装起来,方便将来作为线程入口参数
		{
			std::packaged_task<int(int)> m_package_task(myThread);
			m_package_task(1);
			// std::thread m_thread(std::ref(m_package_task), 1);
			// m_thread.join();

			std::future<int> m_future = m_package_task.get_future();
			std::cout << "return value : " << m_future.get() << std::endl;
		}

		// std::promise:在某个线程给它赋值,然后在其他线程把值取出来
		{
			std::promise<int> m_promise;
			std::thread m_thread1(myThread1, std::ref(m_promise), 10);
			
			m_thread1.join();
			
			//std::future<int> m_future = m_promise.get_future(); // get_future之后,m_promise就不能get_future
			//std::cout << "return value : " << m_future.get() << std::endl;	

			std::thread m_thread2(myThread2, std::ref(m_promise));
			m_thread2.join();
		}

		std::cout << "end main thread id : " << std::this_thread::get_id() << std::endl;

	}
}

// 原子操作
namespace Atomic {
	// 原子操作可以理解为不需要互斥量加锁的多线程并发编程
	// 互斥量加锁一般针对一段代码,原子操作一般针对一个变量
	// int gInt = 0;
	// std::mutex mMutex;
	std::atomic<int> gAtomic = 0; // 封装一个类型为int的对象

	// 原子类型指定 bool、char、int、long、指针等类型作为模板参数(不支持浮点类型和复合类型)。

	void add() {
		for (int i = 0; i < 10000000; ++i) {
			// mMutex.lock();
			// ++gInt;
			// mMutex.unlock();

			++gAtomic;
		}
	}

	void test() {
		std::thread thread1(add);
		std::thread thread2(add);

		thread1.join();
		thread2.join();

		// std::cout << "gInt = " << gInt << std::endl;
		std::cout << "gAtomic = " << gAtomic << std::endl;
	}
}

int main()
{
	// Thread::test();
	Mutex::test();
	// Condition::test();
	// ThreadRetValue::test();
	// Atomic::test();

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值