赞
踩
目录
由于是学习笔记,就不放在项目经验系列里了。
参考资料:
C++多线程详细讲解_千场托儿索的博客-CSDN博客_c++多线程
https://www.jianshu.com/p/5f0bc41249ad
废话不多说。
进程就是程序的实体,比如你打开任务管理器,上面vscode,wegame等等就是应用,一个应用可能包含一个或多个进程,各司其职。一般而言,进程之间是分隔开的,一个崩了不影响其他的,除非进程之间存在通信,那就是多进程编程,但这样有诸如效率低,麻烦等缺点。
而线程就是轻量级的进程,区别在于线程不独立的拥有资源,依赖于创建它的进程而存在,同一进程中的多个线程共享相同的地址空间,可以访问进程中的大部分数据。而一个进程至少拥有一个线程。
应用-进程-线程,就像公司-部门-员工吧。
之前的pthread.h太麻烦,C++11后,就引入了#include<thread>,它在CMakeLists中的引用方式为
- find_package(Threads REQUIRED)
-
- target_link_libraries(${PROJECT_NAME} ${CMAKE_THREAD_LIBS_INIT})
创建和使用多线程的方式如下
- #include <iostream>
- #include <thread>
-
- int k=0;
-
- void foo(int n)
- {
- for(int i=0;i<n;i++)
- {k++;}
- }
-
- void foo_2(int n)
- {
- for(int i=0;i<n;i++)
- {k++;}
- }
-
- int main()
- {
- std::thread Tr1(foo,100);
- std::thread Tr2(foo_2,100);
-
- Tr1.join();
- Tr2.join();
-
- std::cout<<k<<std::endl;
- }
其中,当创建好std::thread对象时,函数foo和foo_2就已经在执行了,下面的join的意思是,如果当main()函数执行到这里,Tr1和Tr2还在执行,就等等,执行完了才往下走(很合理,线程阻塞,等待结果),detach不推荐使用。
然后这里相当于有两个线程在同时执行foo和foo_2函数的,这就提高了效率。
实际写程序,可能遇到如下报错,原因是函数名可能和某个类的成员函数名重合了,成员函数作为传参需加上类名。
error: no matching function for call to 'std::thread::thread ...
然后这里还有个线程安全的问题,即,上面两个线程同时在修改公共资源k,当你把n调大,比如100000,就会发现k不等于2n。
我现在有一段代码,如果我不想专门创建一个函数呢?没关系,可以用匿名函数
std::thread Tr([&](){some codes});
即便这个some codes有千行也可以。
第二个是,我不确定我要创建几个线程,能不能动态决定?
可以,用数组和匿名对象
- std::vector<std::thread> TRs;
- TRs.reserve(num);
-
- for(int i=0;i<num;i++)
- {
- TRs.emplace_back(std::thread([&,=i](){some codes})); // 公共资源要么上锁,要么像i一样使用值传递
- }
-
- for(int i=0;i<num;i++)
- {
- TRs[i].join();
- }
最后问一下,一个进程能创建多少线程?跟默认预留堆栈空间有关,linux下,大约几百到几千吧。
可以通过对资源上锁的方式,杜绝同时访问,高级的我还不懂,初级的手动锁和自动锁如下:
①手动上锁解锁
- // 手动锁
- #include <iostream>
- #include <thread>
- #include <mutex>
-
- std::mutex mtx;
- int k=0;
-
- void foo(int n)
- {
- mtx.lock();
- for(int i=0;i<n;i++)
- {k++;}
- mtx.unlock();
- }
-
- void foo_2(int n)
- {
- mtx.lock();
- for(int i=0;i<n;i++)
- {k++;}
- mtx.unlock();
- }
-
- int main()
- {
- std::thread Tr1(foo,100);
- std::thread Tr2(foo_2,100);
-
- Tr1.join();
- Tr2.join();
-
- std::cout<<k<<std::endl;
- }
②自动锁
- // 自动锁
- #include <iostream>
- #include <thread>
- #include <mutex>
-
- std::mutex mtx;
- int k=0;
-
- void foo(int n)
- {
- std::lock_guard<std::mutex> lock(mtx);
- for(int i=0;i<n;i++)
- {k++;}
- }
-
- void foo_2(int n)
- {
- std::lock_guard<std::mutex> lock(mtx);
- for(int i=0;i<n;i++)
- {k++;}
- }
-
- int main()
- {
- std::thread Tr1(foo,100);
- std::thread Tr2(foo_2,100);
-
- Tr1.join();
- Tr2.join();
-
- std::cout<<k<<std::endl;
- }
然后需要注意的是,我们说的同时访问指的是真的同一处,比如你定义一个公共资源结构体,两个线程同时访问同一结构体实例中的不同成员,其实是没有影响的(个人实验,不对请指教)。
互斥锁虽然保证了安全,但性能开销挺大的,所以C++11引入了原子,即,直接把公共资源设为原子类型,它会自动保证同时只有一个线程访问修改,不需要加锁解锁,代码如下
- // 原子
- #include <iostream>
- #include <thread>
- #include <mutex>
- #include <atomic>
-
- std::atomic<int> k=0;
-
- void foo(int n)
- {
- for(int i=0;i<n;i++)
- {k++;}
- }
-
- void foo_2(int n)
- {
- for(int i=0;i<n;i++)
- {k++;}
- }
-
- int main()
- {
- std::thread Tr1(foo,100);
- std::thread Tr2(foo_2,100);
-
- Tr1.join();
- Tr2.join();
-
- std::cout<<k<<std::endl;
- }
当然原子的玩法有很多,暂时没探究了,总而言之,它的性能开销比互斥锁加锁解锁小,也能保证安全。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。