2000字范文,分享全网优秀范文,学习好帮手!
2000字范文 > C++多线程快速入门(一):基本常用操作

C++多线程快速入门(一):基本常用操作

时间:2020-08-30 11:58:49

相关推荐

C++多线程快速入门(一):基本常用操作

目录

case1:创建线程1 join、detachcase2:创建线程2 线程传参 传值或者传引用case3:创建线程 线程传参 functional object作为参数case4:观察多线程程序加速计算case5:future + get 获取并发结果case6:互斥锁case7:std::lock_guard 类模板

case1:创建线程1 join、detach

创建线程,并等该线程执行完毕,并且打印两个线程的id

#include <iostream>#include <thread>using namespace std;void func() {cout << "hello , this is my thread, thread id is " << this_thread::get_id() << endl;}int main() {thread th = thread(func);th.join();cout << "this is main thread and its id is " << this_thread::get_id() << endl;}

执行结果如下:

C:\Users\LENOVO\CLionProjects\untitled\cmake-build-debug\untitled.exehello , this is my thread, thread id is 2this is main thread and its id is 1Process finished with exit code 0

使用detach,放弃对该线程的控制:

#include <iostream>#include <thread>using namespace std;void func() {cout << "hello , this is my thread, thread id is " << this_thread::get_id() << endl;}int main() {thread th = thread(func);th.detach();// 如果我们此时不再关系该线程的运行情况的话可以使用detachcout << th.joinable() << endl;cout << "this is main thread and its id is " << this_thread::get_id() << endl;}

运行结果:

C:\Users\LENOVO\CLionProjects\untitled\cmake-build-debug\untitled.exe0this is main thread and its id is 1hello , this is my thread, thread id is 2Process finished with exit code 0

case2:创建线程2 线程传参 传值或者传引用

传值

#include <iostream>#include <thread>using namespace std;void func(string s) {cout << "hello , this is my thread, thread arg is " << s << endl;}int main() {thread th = thread(func, "test");th.join();cout << "this is main thread and its id is " << this_thread::get_id() << endl;}

打印结果如下:

C:\Users\LENOVO\CLionProjects\untitled\cmake-build-debug\untitled.exehello , this is my thread, thread arg is testthis is main thread and its id is 1Process finished with exit code 0

传引用

#include <iostream>#include <thread>using namespace std;void func(string& s) {cout << (&s) << endl;cout << "hello , this is my thread, thread arg is " << s << endl;}int main() {string str = "test";thread th = thread(func, ref(str));cout << (&str) << endl;th.join();cout << "this is main thread and its id is " << this_thread::get_id() << endl;}

打印结果如下:

C:\Users\LENOVO\CLionProjects\untitled\cmake-build-debug\untitled.exe0x62fd100x62fd10hello , this is my thread, thread arg is testthis is main thread and its id is 1Process finished with exit code 0

case3:创建线程 线程传参 functional object作为参数

关于仿函数的定义可以看:仿函数

如果单纯地将逻辑函数传入thread,在函数逻辑比较复杂的时候不太好。

将函数封装到类的内部,可以赋予一定的封装性,并且可以在类内部创建例如map的数据结构来记录函数运行的状态。

仿函数、不带参

#include <iostream>#include <thread>using namespace std;// functional objectstruct A {void operator()() {cout << "I'm A" << endl;}};void show() {cout << "I'm show" << endl;}int main() {show();A a;a(); // 等价于 a.operator()();// 我们把这种object称为callable的object ,又称为仿函数thread thread1 = thread(A());thread1.join();return 0;}

打印结果如下:

C:\Users\LENOVO\CLionProjects\untitled\cmake-build-debug\untitled.exeI'm showI'm AI'm AProcess finished with exit code 0

仿函数、带参

#include <iostream>#include <thread>using namespace std;// functional objectstruct A {void operator()(int num) {for (int i = 0; i < num; i++) {cout << "I'm A" << i << endl;}}};int main() {int num = 10;thread thread1 = thread(A(), num);for (int i = 0; i < num; i++) {cout << "I'm main" << i << endl;}thread1.join();return 0;}

打印结果如下,是乱序的,符合多线程特性

并且 cout 并不是线程安全的,所以可以发现,有乱码的样子

C:\Users\LENOVO\CLionProjects\untitled\cmake-build-debug\untitled.exeI'm mainI'm A00I'm A1I'm A2I'm A3I'm AI'm main41I'm mainI'm A25I'm mainI'm A36I'm AI'm main47I'm main5I'm mainI'm A86I'm mainI'm A97I'm main8I'm main9Process finished with exit code 0

lambda函数作为参数

#include <iostream>#include <thread>using namespace std;int main() {string s = "test";thread f = thread([&s](int a,int b) {cout << s << endl;cout << a + b << endl;}, 2, 3);f.join();return 0;}

打印结果:

C:\Users\LENOVO\CLionProjects\untitled\cmake-build-debug\untitled.exetest5Process finished with exit code 0

case4:观察多线程程序加速计算

#include <iostream>#include <thread>using namespace std;// 测量一个函数的运行时间template <class T>void measure(T&& func) {using namespace std::chrono;auto start = system_clock::now();// funcfunc();duration<double> diff = system_clock::now() - start;cout << "执行了" << diff.count() << "秒" << endl;}// 求和函数[start,end)void sum(long start, long end, long& ans) {long s = 0;for(auto i = start; i < end; i++) {s += i;}ans = s;}const long S = 100000000;int main() {// 测量一下把工作分摊给两个线程做的时间measure([](){long ans1, ans2;thread t1 = thread(sum, 0, S >> 1, std::ref(ans1));thread t2 = thread(sum, S >> 1, S, std::ref(ans2));t1.join();t2.join();cout << "ans = " << ans1 + ans2 << endl;});// 测量一下单线程工作时间measure([](){long ans;sum(0, S, ans);cout << "ans = " << ans << endl;});return 0;}

打印结果:

C:\Users\LENOVO\CLionProjects\untitled\cmake-build-debug\untitled.exeans = 887459712执行了0.13546秒ans = 887459712执行了0.240006秒Process finished with exit code 0

当然这里有一个细节:

如果在多线程程序中我们传入了一个ref引用,在线程程序中我们最好不要直接对这个ref值操作,而是采用一个临时变量,例如上面的程序中我们就使用了一个临时变量s,对s进行累加后再把值赋给ans。这样会提高程序运行效率。

case5:future + get 获取并发结果

future包裹线程运行结果,使用方法如下:

由于我们线程函数的结果是long,所以首先定义future<long>

#include <iostream>#include <thread>#include <future>#include <vector>using namespace std;// 测量一个函数的运行时间template <class T>void measure(T&& func) {using namespace std::chrono;auto start = system_clock::now();// funcfunc();duration<double> diff = system_clock::now() - start;cout << "执行了" << diff.count() << "秒" << endl;}// 求和函数[start,end)long sum(long start, long end) {long s = 0;for(auto i = start; i < end; i++) {s += i;}return s;}const long S = 100000000;int main() {// 测量一下把工作分摊给 threadNums 个线程做的时间measure([](){const long threadNums = 8;vector<future<long>> vec;vec.reserve(threadNums);for (int i = 0; i < threadNums; i++) {vec.push_back(async(sum, (S / threadNums) * i, (S / threadNums) * (i + 1)));}long ans = 0;// get 阻塞式地拿到并发结果for (int i = 0; i < threadNums; i++) {ans += vec[i].get();}cout << "ans = " << ans << endl;});// 测量一下单线程工作时间measure([](){long ans = sum(0, S);cout << "ans = " << ans << endl;});return 0;}

运行结果如下:

C:\Users\LENOVO\CLionProjects\untitled\cmake-build-debug\untitled.exeans = 887459712执行了0.0521455秒ans = 887459712执行了0.250044秒Process finished with exit code 0

case6:互斥锁

多个线程访问同一个值并且对其修改,会导致数据竞争。:

#include <iostream>#include <thread>#include <future>#include <vector>using namespace std;// 测量一个函数的运行时间template <class T>void measure(T&& func) {using namespace std::chrono;auto start = system_clock::now();// funcfunc();duration<double> diff = system_clock::now() - start;cout << "执行了" << diff.count() << "秒" << endl;}std::mutex mtx;// 求和函数 线程安全的void sum(long& s) {mtx.lock();for (int i = 0; i < 100000; i++) {s++;}mtx.unlock();}int main() {// 测量一下把工作分摊给 threadNums 个线程做的时间measure([](){vector<thread> v;long s = 0;for (int i = 0; i < 4; i++) {v.emplace_back(std::thread(sum, std::ref(s)));}for (int i = 0; i < 4; i++) {v[i].join();}cout << "ans " << s << endl;});measure([](){long s = 0;for (int i = 0; i < 4; i++) {sum(s);}cout << "ans " << s << endl;});return 0;}

测试结果:

C:\Users\LENOVO\CLionProjects\untitled\cmake-build-debug\untitled.exeans 400000执行了0.0141654秒ans 400000执行了0.0155926秒Process finished with exit code 0

case7:std::lock_guard 类模板

如果mtx.lock()和mtx.unlock()之间的语句发生了异常,unlock()语句没有机会执行!

这会导致mtx一直处于锁着的状态,其他使用sum函数的线程就会阻塞。

c++库已经提供了std::lock_guard类模板,构造时自动加锁,析构时自动解锁:

将case6的sum函数修改为下面形式即可:

// 求和函数 线程安全的void sum(long& s) {std:;lock_guard<std::mutex> guard(mtx);// mtx.lock();for (int i = 0; i < 100000; i++) {s++;}// mtx.unlock();}

如果想自己实现的lock_guard的话也可以:

class mutexLockGuard{private:std::mutex& mtx;public:explicit mutexLockGuard(std::mutex& mutex) : mtx(mutex){mtx.lock();}~mutexLockGuard() {mtx.unlock();}};

笔记参考:

C++多线程快速入门

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。