本文最后更新于:2024年5月7日 下午
线程也叫轻量级进程,通常一个进程包含若干个线程。线程可以利用进程所拥有的资源。 本文记录 C++ 操作线程的方法。
并发
两个或者多个独立的活动同时进行的现象称为并发。并发可以简单的认为,可以理解成多个应用程序同时运行。在单核CPU中,并发实际上是一种假象,进程之间实际上是按照一定的分配算法轮流使用CPU。
并发的实现主要有两种方式:
- 多进程实现并发
- 单个进程,多个线程实现并发,就是一个主线程多个子线实现。
thread
C++ 11 之后添加了新的标准线程库 std::thread
,用于线程控制,std::thread
在 <thread>
头文件中声明,因此使用 std::thread
时需要包含 在 <thread>
头文件。
std::thread
默认构造函数,创建一个空的 std::thread
执行对象。
1 2
| #include<thread> std::thread thread_object(callable)
|
一个可调用对象可以是以下三个中的任何一个:
创建线程
不带参的方式创建线程
不带参数的普通函数作为线程处理函数。
1 2 3 4 5 6 7 8 9 10 11 12
| #include<iostream> #include<thread> using namespace std; void print(){ cout<<"子线程在运行。。。"<<endl; } int main(){ thread t1(print); cout<<"主线程。。。"<<endl; return 0; }
|
通过类和对象创建线程
利用类中的仿函数作为线程处理函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include<iostream> #include<thread> using namespace std; class A{ void operator()(){ cout<<"子线程..."<<endl; } }; int main(){ A a; thread t1(a); t1.join();
cout<<"主线程..."<<endl; }
|
通过Lambda表达式创建线程
Lambda 表达式简单地说,就是将函数定义和调用放在一处实现。
1 2 3 4 5 6 7 8
| #include<iostream> #include<thread> using namespace std; int main(){ thread t1([]{cout<<"子线程调用..."<<endl;}); t1.join(); cout<<"主线程..."<<endl; }
|
带参的方式创建线程
将带参数的函数作为线程处理函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #include<iostream> #include<thread> using namespace std; void p1(int &n){ cout<<"子线程"<<n<<endl; n++; } int main(){ int n=0; thread t3(p1,std::ref(n)); t3.join(); thread t31(p1,std::ref(n)); t31.join(); cout<<"主线程..."<<endl; }
|
当有多个参数时可以直接接在线程的构造函数参数中,他会在后台默默运行
如果测试时设置了断点发现测试日志没有成功输出,可能是子线程还没有开始运转,可以加个 while(1){;}
再观察结果。
智能指针的方式创建线程
就是以智能指针为参数的函数作为线程处理函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| void p2(unique_ptr<int> ptr){ cout<<"子线程:"<<ptr.get()<<endl; cout<<"子线程id: "<<this_thread::get_id()<<endl; }
int main(){ int *p=new int(12); cout<<*p<<endl; unique_ptr<int> ptr(new int(1000)); cout<<"主线程:"<<ptr.get()<<endl; thread t4(p2,move(ptr)); t4.join(); cout<<"主线程id: "<<this_thread::get_id()<<endl; cout<<"主线程..."<<ptr.get()<<endl; }
|
类的成员函数创建线程
将类的成员函数作为线程处理函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class B{ public: void p3(int &num){ num=1100; cout<<"子线程id:"<<this_thread::get_id()<<endl; } }; int main(){ B b; int num=10; thread t5(&B::p3,b,ref(num)); t5.join(); cout<<"主线程id: "<<this_thread::get_id()<<endl; }
|
使用方法
线程运行函数
创建线程:调用 thread 类去调用一个线程的对象,添加处理函数作为线程的任务
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include<iostream> #include<thread> using namespace std; void print(){ cout<<"子线程在运行。。。"<<endl; } int main(){ thread t1(print); cout<<"主线程。。。"<<endl; return 0; }
|
子线程可能正在运行时主线程已经退出了,可能会报出异常退出的错误,这就需要将线程的调度整合起来。
join( )
可以利用 join 函数加入,汇合线程,阻塞主线程。添加以后等线程运行结束之后才运行主线程。
注意: 一个线程只能 join 一次,不能重复。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #include<iostream> #include<thread> #include<windows.h> using namespace std;
void print(){ Sleep(2000); cout<<"子线程在运行..."<<endl; } int main(){ thread t1(print); t1.join(); cout<<"主线程..."<<endl; return 0; }
|
输出
detach( )
detach( )
函数用于打破主线程和子线程之间的依赖关系,将子线程和主线程之间进行分离,不影响。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include<iostream> #include<thread> #include<windows.h> using namespace std;
void print() { Sleep(2000); cout << "子线程在运行..." << endl; } int main() { thread t1(print); t1.detach(); cout << "主线程..." << endl; return 0; }
|
输出
joinable( )
joinable( )
函数是一个布尔类型的函数,他会返回一个布尔值来表示当前的线程是否是可执行线程(能被 join
或者 detach
)
因为相同的线程不能 join
两次,也不能 join
完再 detach
, 同理也不能 detach
, 所以 joinable
函数就是用来判断当前这个线程是否可以 join
的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #include<iostream> #include<thread> using namespace std; void print(){ cout<<"子线程在运行。。。"<<endl; } int main(){ thread t1(print); t1.join(); cout<<"主线程..."<<endl; if(t1.joinable()) cout<<"能join"<<endl; else cout<<"不能进行join"<<endl; return 0; }
|
输出
1 2 3
| 子线程在运行。。。 主线程... 不能进行join
|
get_id()
获取当前线程 id
1
| std::this_thread.get_id()
|
参考资料
文章链接:
https://www.zywvvd.com/notes/coding/cpp/cpp-thread/cpp-thread/