c/c++语言开发共享C++多线程编程超详解

目录c++多线程2. 常用api4. windows多线程4.1 windows创建线程4.2 windows互斥锁4.3 windows挂起和唤醒线程c++多线程1. 概念 进程:一个在内存中

目录
  • c++多线程
  • 2. 常用api
  • 4. windows多线程
    • 4.1 windows创建线程
    • 4.2 windows互斥锁
    • 4.3 windows挂起和唤醒线程

    c++多线程

    1. 概念

    • 进程:一个在内存中运行的应用程序。每个进程都有自己独立的一块内存空间,一个进程可以有多个线程,比如在windows系统中,一个运行的xx.exe就是一个进程。
    • 线程:进程中的一个执行任务(控制单元),负责当前进程中程序的执行。一个进程至少有一个线程,一个进程可以运行多个线程,多个线程可共享数据。与进程不同的是同类的多个线程共享进程的堆和方法区资源,但每个线程有自己的程序计数器、虚拟机栈和本地方法栈,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。
    • 并发:并发指的是两个或多个独立的活动在同一时段内发生。并发在生活中随处可见:比如在跑步的时候同时听音乐,在看电脑显示器的同时敲击键盘等。同一时间段内可以交替处理多个操作,强调同一时段内交替发生。
    • 并行:同一时刻内同时处理多个操作,强调同一时刻点同时发生。

    2. 常用api

    ​ 头文件#include<thread>

    1.thread

    api 描述 注意
    thread.join() 加入线程(会阻塞主线程,模拟同步操作)
    thread.detach() 加入线程(不会阻塞主线程,模拟异步操作)
    thread.joinable() 是否可加入线程,返回bool
    thread.get_id() 获取线程的id
    thread.hardware_concurrency() 获取硬件并发的数量
    thread.swap() 交换线程
    thread.native_handle() 获取原生handle,为windows多线程中createthread的返回值,使用这个handle从而可以实现线程的挂起唤醒

    测试代码:

      void threadfunc01() {  	cout << "thread join1" << endl;  	this_thread::sleep_for(chrono::seconds(2));  }  void threadfunc02() {  	cout << "thread join2" << endl;  	this_thread::sleep_for(chrono::seconds(2));  }  void test01() {  	// 创建线程  	std::thread thread1(threadfunc01);  	std::thread thread2(threadfunc02);  	//thread.join(); //join 会阻塞主线程 同步操作  	//thread.detach(); //detach 不会阻塞主线程 异步操作  	bool bjoinable = thread1.joinable();  	thread::id threadid = thread1.get_id();  	//hardware_concurrency 硬件并发的数量  	int threadnum = thread1.hardware_concurrency();  	cout << "hardware_concurrency:" << threadnum << endl;  	//应用 线程的预分配。  	for (int i = 0; i < thread1.hardware_concurrency(); i++) {  		std::thread threadref(threadfunc01);  		threadref.detach();  	}  	thread1.swap(thread2);  	thread1.join();  }  

    向线程里传递参数的方法

      // 向线程里传递参数的方法  #include<string>  void threadfunc03(int num, const string& str) {  	cout << "num = " << num << " str = " << str << endl;  }  struct fobject {  	void run(const string& str) {  		cout << str << endl;  	}  };  void test02() {      // 通过函数绑定  	thread newthread1(threadfunc03, 10, "unreal");  	newthread1.detach();  	// 通过lambda绑定  	int a = 50;  	thread newthread2([&](int num,const string& str) {  		cout << "a = " << a << " num = " << num << " str = " << str << endl;  		}, 1, "unreal");  	newthread2.detach();    	// 绑定对象  	fobject objectref;  	thread newthread3(&fobject::run, objectref, "unreal");  	newthread3.detach();  }  

    2.互斥锁mutex

    ​ 头文件#include<mutex>

    api 描述 注意
    mutex.lock() 上锁
    mutex.unlock() 解锁
    mutex.try_lock() 判断可不可以加锁,返回bool 可以用该方法建立非阻塞模式

    测试代码:

      #include<mutex>  mutex lockref;  void threadfunc04(int num,const string& str) {  	// 进入该线程锁住该线程,其他线程想要进入该线程需要排队  	lockref.lock();  	cout << "thread join4" << endl;  	this_thread::sleep_for(chrono::seconds(2));  	// 解锁  	lockref.unlock();  }  void test03() {  	std::thread thread1(threadfunc04, 10, "unreal");  	std::thread thread2(threadfunc04, 5, "unity");  	std::thread thread3(threadfunc04, 20, "cocos");  	thread1.detach();  	thread2.detach();  	thread3.detach();  }  

    使用类加锁的方式:

      #include<mutex>  mutex lockref;  struct fevent {  	fevent() {  		m.lock();  	}  	~fevent()  	{  		m.unlock();  	}  	static mutex m;  };  mutex fevent::m;  #define lock_scope fevent event  void threadfunc04(int num,const string& str) {  	lock_scope; //加上锁,并且过了这个作用域自动解锁(析构)  	cout << "thread join4" << endl;  	this_thread::sleep_for(chrono::seconds(2));  }  void test03() {  	std::thread thread1(threadfunc04, 10, "unreal");  	std::thread thread2(threadfunc04, 5, "unity");  	std::thread thread3(threadfunc04, 20, "cocos");  	thread1.detach();  	thread2.detach();  	thread3.detach();  }  

    try_lock()

      void threadfunc04(int num,const string& str) {  	bool block = fevent::m.try_lock();  	if (block) {  		lock_scope; //加上锁,并且过了这个作用域自动解锁(析构)  		cout << "thread join4" << endl;  		this_thread::sleep_for(chrono::seconds(2));  	}  }  

    ​ 使用try_lock()可以进行判断能不能上锁,不能上锁的话,就不用执行上锁后的代码,防止其他线程阻塞在该线程。

    lock_guard

    lock_guard是一种锁类,作用和我们上面自定义的锁类fevent相同,创建的时候锁住目标线程,释放的时候解锁。

      // 声明方式  lock_guard<mutex>ref;  

    源码:

      template <class _mutex>  class lock_guard { // class with destructor that unlocks a mutex  public:      using mutex_type = _mutex;      explicit lock_guard(_mutex& _mtx) : _mymutex(_mtx) { // construct and lock          _mymutex.lock();      }      lock_guard(_mutex& _mtx, adopt_lock_t) : _mymutex(_mtx) { // construct but don't lock      }      ~lock_guard() noexcept {          _mymutex.unlock();      }      lock_guard(const lock_guard&) = delete;      lock_guard& operator=(const lock_guard&) = delete;  private:      _mutex& _mymutex;  };  

    unique_lock

    ​ 作用和lock_guard相同,唯一的不同之处,lock_guard开放的api只有析构函数,而unique_lock开放的api非常多,即自由度比lock_guard高,可以定义锁的行为。

      void test05() {  	// defer_lock 关键字为延迟锁,即创建该对象时不会锁住该线程,什么时候锁需要自定义  	std::unique_lock<mutex>lockref2(fevent::m,defer_lock);  	std::unique_lock<mutex>lockref2(fevent::m,chrono::seconds(2)); //锁两秒  	//....执行  	lockref2.lock();  	lockref2.unlock();  	bool block1 = lockref2.try_lock();//尝试上锁  	lockref2.try_lock_for(chrono::seconds(2)); //锁2s      mutex *lockref3 = lockref2.release(); //释放锁,同时会返回被释放的这个锁的指针对象      bool block2 = lockref2.owns_lock(); //当前是否被锁住   }  

    应用:

      void test05() {  	//std::lock_guard<mutex>lockref1(fevent::m);  	// defer_lock 关键字为延迟锁  	std::unique_lock<mutex>lockref2(fevent::m,defer_lock);  	lockref2.lock();  	lockref2.mutex();  	bool block = lockref2.owns_lock();  	std::unique_lock<mutex>lockref3;  	lockref2.swap(lockref3);  	std::unique_lock<mutex>lockref4 = move(lockref3);  	lockref4.unlock();  }  

    3. 挂起和唤醒

    ​ 头文件#include<windows.h>

    1111111

    111111

    111111

    11111

    111111

    测试代码:

      #include<windows.h>  void threadfunc05() {  	while (true)  	{  		sleep(10);  		cout << "threadfunc05" << endl;  	}  }    void test04() {  	thread thread1(threadfunc05);  	// 挂起线程  	suspendthread(thread1.native_handle());  	sleep(2);  	// 唤醒线程  	resumethread(thread1.native_handle());  }  

    如何高效将主线程资源进行转移:

      void threadfunc06(const char* str) {  	cout << str << endl;  }  void test04() {  	// 如何高效转移线程资源  	// 使用std::move  	thread thread2(threadfunc06, move("unreal")); // 使用move避免了拷贝  	thread thread3 = move(thread2);  	thread3.detach();  }  

    3. 应用场景

    3.1 call_once执行一次的函数

    ​ 通过使用该函数,用来防止多线程的多次触发。

      once_flag tag;  void calloncetest() {  	call_once(tag, [&]() {  		cout << "do once" << endl;  		});  }  void test06() {  	for (int i = 0; i < 10; i++) {  		thread thread1(calloncetest);  		thread1.detach();  	}  }  

    3.2 condition_variable条件锁

    ​ 使用需要包含头文件#include<condition_variable>

    可以使用条件锁来达到同步的作用,即当满足一定的条件后才解锁某个线程。

      #include<condition_variable>  condition_variable condition_lock;  mutex mutexlock;  void conditionfunctest() {  	unique_lock<mutex>lock(mutexlock);  	condition_lock.wait(lock);  //锁住该线程  	cout << "run" << endl;  }  void test12() {  	std::thread threadref(conditionfunctest);  	threadref.detach();  	sleep(3000); //3s后再激活  	condition_lock.notify_one();  }  

    3.3 future获取线程的计算结果

    ​ 通过使用future可以得到”未来”线程被调用的时候计算得返回值,使用时需要包含头文件#include<future>。

    声明方式:

      // async为创建该线程的方式为异步 funname 函数名 args为传入的函数参数  std::future<string>newfuture = std::async(launch::async, funname,args...);  

    应用:

      #include<future>   string getstring(int num) {  	return "unreal";  }  void test08() {  	std::future<string>newfuture = std::async(launch::async, getstring, 10);  	//std::future<string>newfuture = std::async(launch::deferred, getstring, 10); // 睡一秒再执行  	sleep(1000);  	string str = newfuture.get(); //get只能调用一次 调第二次会崩溃  	// 防止崩溃的写法  	if (newfuture.valid()) {  		string str = newfuture.get();  	}  }  

    3.4 promise主线程如何将数据发送数据到其他线程

    ​ 通过使用promise(承诺)来进行进程之间的交互,常配合std::future使用。其作用是在一个线程t1中保存一个类型typename t的值,可供相绑定的std::future对象在另一线程t2中获取。

    ​ 测试代码:

      // promise  string promisetest(future<string>& future) {  	cout << future.get() << endl;  	return "unreal";  }  void test09() {  	promise<string> promiseref;  	future<string>future1 = promiseref.get_future();  	future<string>future2 = std::async(launch::async, promisetest, std::ref(future1)); //future 不支持值拷贝 需要传递引用  	promiseref.set_value("unreal is the best game engine in the world");  }  

    ​ 但这里也有一个问题需要思考,如果需要发送数据到多个线程,是不是需要一个个的创建上面的代码呢。这里就引出了多线程之间共享状态这个解决方法。

    3.5 future.share()多线程之间共享状态

    ​ 通过future.share()我们可以很方便的使多个线程之间共享状态。

    现在来看看没有使用该函数的话我们要共享状态的话需要这么写:

      string promisetest(future<string>& future) {  	cout << future.get() << endl;  	return "unreal";  }  void test09() {  	promise<string> promiseref;  	future<string>future1 = promiseref.get_future();  	future<string>future2 = promiseref.get_future();  	future<string>future3 = promiseref.get_future();  	future<string>future4 = std::async(launch::async, promisetest, std::ref(future1)); //future 不支持值拷贝 需要传递引用  	future<string>future5 = std::async(launch::async, promisetest, std::ref(future2)); //future 不支持值拷贝 需要传递引用  	future<string>future6 = std::async(launch::async, promisetest, std::ref(future3)); //future 不支持值拷贝 需要传递引用  	promiseref.set_value("unreal is the best game engine in the world");  }  

    使用了future.share()函数后:

      string promisetest02(shared_future<string> future) {  	cout << future.get() << endl;  	return "unreal";  }  void test09() {  	promise<string> promiseref;  	future<string>future1 = promiseref.get_future();      // shared_future  	shared_future<string> sharedfutrue1 = future1.share();  	future<string>future2 = std::async(launch::async, promisetest02, sharedfutrue1); //shared_future 可以用拷贝传递  	future<string>future3 = std::async(launch::async, promisetest02, sharedfutrue1);  	future<string>future4 = std::async(launch::async, promisetest02, sharedfutrue1);  	promiseref.set_value("unreal is the best game engine in the world");  }  

    3.6 线程packaged_task

    ​ packaged_taskpromise非常相似,packaged_task<f>是对promise<t= std::function<f>>中t= std::function<f>这一可调对象(如函数、lambda表达式等)进行了包装,简化了使用方法。并将这一可调对象的返回结果传递给关联的future对象。

    绑定lambda

      void test10() {  	//绑定lambda  	packaged_task<int(int, int)> task1([](int a,int b) ->int{  		return a + b;  		});  	task1(1, 4);  	this_thread::sleep_for(chrono::seconds(1));  	if (task1.valid()) {  		auto f1 = task1.get_future();  		cout << f1.get() << endl;  	}  }  

    绑定普通函数

      int packagedtest(int a,int b) {  	return a + b;  }  void test10() {  	//绑定函数  	packaged_task<int(int, int)>task2(packagedtest);  	task2(10, 5);  	this_thread::sleep_for(chrono::seconds(1));  	if (task2.valid()) {  		auto f2 = task2.get_future();  		cout << f2.get() << endl;  	}  }  

    使用std::bind进行函数绑定

      int packagedtest(int a,int b) {  	return a + b;  }  void test10() {  	// bind  	packaged_task<int(int, int)>task3(std::bind(packagedtest,1,2));  	task3(10, 5); //因为bind使用了占位符 所以这里传入的10 5失效了  	this_thread::sleep_for(chrono::seconds(1));  	if (task3.valid()) {  		auto f3 = task3.get_future();  		cout << f3.get() << endl; //1+2  	}  }  

    3.7 时间约束

      void test11() {  	//休眠2s  	this_thread::sleep_for(chrono::seconds(2));  	// 休眠现在的时间加上2s  	chrono::steady_clock::time_point timepos = chrono::steady_clock::now() + chrono::seconds(2);  	this_thread::sleep_until(timepos);  }  

    4. windows多线程

    ​ 使用windowsapi进行多线程的编写,需要包含头文件

      #include<windows.h>  

    4.1 windows创建线程

    ​ 使用createthread()创建线程

      dword winapi functhread(lpvoid lppram) {      // dword 类型为unsigned long      // lpvoid 类型为void      cout << "unreal!" << endl;      sleep(1000);      return 0l;  }  void windowsthreadtest01() {  	handle handleref = createthread(nullptr,0, functhread,nullptr,0,nullptr);      sleep(2000);      closehandle(handleref); //使用之后需要关闭handle  }  

    ​ 其中传入的参数为:

      /*  winbaseapi  _ret_maybenull_  handle  winapi  createthread(      _in_opt_ lpsecurity_attributes lpthreadattributes,  和线程安全有关 一般为null      _in_ size_t dwstacksize,                            线程栈的大小      _in_ lpthread_start_routine lpstartaddress,         被线程执行的回调函数      _in_opt_ __drv_aliasesmem lpvoid lpparameter,       传入线程的参数      _in_ dword dwcreationflags,                         创建线程的标志   参数0 代表立即启动该线程      _out_opt_ lpdword lpthreadid                        传出的线程id  );  */  

    4.2 windows互斥锁

      // windows互斥锁  handle hmutex = nullptr;  dword winapi functhread02(lpvoid lpparam) {      cout << "unreal" << endl;      waitforsingleobject(hmutex, infinite);      sleep(5000);      releasemutex(hmutex);      return 0l;  }    void windowsthreadtest02() {      hmutex = createmutex(nullptr, false, l"mutex");      handle handleref1 = createthread(nullptr, 0, functhread02, nullptr, 0, nullptr);      handle handleref2 = createthread(nullptr, 0, functhread02, nullptr, 0, nullptr);      closehandle(handleref1);      closehandle(handleref2);  }  

    传入的参数为:

      /*  winbaseapi  _ret_maybenull_  handle  winapi  createmutexw(      _in_opt_ lpsecurity_attributes lpmutexattributes,      和线程安全有关一般为null      _in_ bool binitialowner,                               有没有该锁的控制权      _in_opt_ lpcwstr lpname                                锁免费精选名字大全      );  */  

    4.3 windows挂起和唤醒线程

    ​ 通过使用suspendthread(handleref)和resumethread(handleref)来挂起和唤醒线程

      // windows 挂起唤醒  dword winapi functhread03(lpvoid lpparam) {      while (true) {          sleep(500);          cout << "isrunning" << endl;      }      return 0l;  }    void windowsthreadtest03() {      handle href = createthread(nullptr, 0, functhread03, nullptr, 0, nullptr);      suspendthread(href);      sleep(2000);      resumethread(href);      closehandle(href);  }  

    总结

    本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注<计算机技术网(www.ctvol.com)!!>的更多内容!

    需要了解更多c/c++开发分享C++多线程编程超详解,都可以关注C/C++技术分享栏目—计算机技术网(www.ctvol.com)!

    本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

    ctvol管理联系方式QQ:251552304

    本文章地址:https://www.ctvol.com/c-cdevelopment/838161.html

    (0)
    上一篇 2021年10月5日
    下一篇 2021年10月5日

    精彩推荐