序言
在上一篇文章中,我们介绍了 线程的概念。在这篇文章中,我们主要介绍在 Linux 系统下对线程的控制。
通过上一篇的学习,我们了解到,在 Linux 中,是不存在真正意义的线程的,只有轻量级进程,所以我们需要借助 POSIX线程库
。
1. 线程的创建
线程的创建我们需要使用函数:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
,参数看起来还是比较多的,我们来一一解释一下:
pthread_t *thread
:指向pthread_t
类型的指针,用于存储新创建的线程的标识符,用于唯一标识一个线程。const pthread_attr_t *attr
:指向线程属性对象的指针,用于设置线程的各种属性(如堆栈大小、调度策略等)。如果传递NULL
,则使用默认属性。(大多数时候,我们使用默认就够了
)void *(*start_routine) (void *)
:新线程将要执行的函数的指针。这个函数必须接受一个void *
类型的参数,并返回一个void *
类型的值。这个参数和返回值可以用来在线程之间传递数据。void *arg
:传递给start_routine
函数的参数。- 返回值:成功返回 0,失败返回错误。
举个栗子:
c
#include <iostream>
#include <string>
#include <cerrno>
#include <unistd.h>
#include <pthread.h>
void* thread_Func(void* arg) {
int* ptr = static_cast<int*>(arg); // 更安全地将 void* 转换为 int*
if (ptr != nullptr) {
std::cout << "Hello, I am thread_" << *ptr << std::endl;
sleep(1); // 暂停 1 秒
}
return nullptr;
}
int main()
{
int id = 0;
pthread_t tid = 0;
// 创建线程
int ret = pthread_create(&tid, NULL, thread_Func, &id);
if(ret != 0)
{
perror("pthread_create");
exit(1);
}
return 0;
}
一个简单的创建线程就完成啦,现在我们使用 g++ TestPthread.cc -o TestPthread
该指令生成可执行文件,发现报错了,错误信息是:
g++ TestPthread.cc -o TestPthread
/usr/bin/ld: /tmp/ccgvZbpw.o: in function
main': TestPthread.cc:(.text+0xb0): undefined reference to
pthread_create'collect2: error: ld returned 1 exit status
make: *** [Makefile:2: TestPthread] Error 1
在之前我们学习动静态库中也出现过这个情况,这是因为 g++不认识这个库
,所以默认不会链接 pthread
库,因此你需要显式地告诉编译器链接:
g++ TestPthread.cc -o TestPthread -l pthread
。
2. 线程的退出
线程的退出,我们有三个方式:
2.1 return
函数
当一个线程执行完毕时,就会执行熟悉的 return
函数退出线程。
2.2 pthread_exit
函数
该函数 void pthread_exit(void *value_ptr);
可以和 return
函数达到平替的效果。
2.3 pthread_cancel
函数
前两者都是自己退出,但是这个函数可以使指定线程进行退出:
int pthread_cancel(pthread_t thread);
thread
: 想要终止线程的ID
- 返回值:成功返回 0 ;失败返回错误码
举个栗子:
cpp
.............
// 创建线程
int ret = pthread_create(&tid, NULL, thread_Func, &id);
if(ret != 0)
{
perror("pthread_create");
exit(1);
}
if(pthread_cancel(tid) != 0)
{
perror("pthread_create");
exit(1);
}
.............
2.4 相关注意
注意,线程的退出不能使用 exit
函数 !!!因为它不仅会终止当前线程,还会终止整个进程。
想象一下,大家执行得好好的,一个人跑完了,一退出结果把大家全拉下水了。
3. 线程的等待
和进程一样,线程在执行完毕时,也是需要回收的,不然 已经退出的线程,其空间没有被释放,仍然在进程的地址空间内。
我们使用函数:
int pthread_join(pthread_t thread, void **value_ptr);
thread
:这是要等待的线程的标识符(ID
)。value_ptr
:这是一个指向指针的指针,用于接收被等待线程的退出状态。如果调用者对此不感兴趣,可以传递NULL
。- 返回值:成功返回 0,失败返回错误码。
举个栗子:
cpp
#include <cerrno>
#include <unistd.h>
#include <pthread.h>
#include <iostream>
#include <pthread.h>
#include <unistd.h> // 包含 sleep 函数的定义
void* thread_Func(void* arg)
{
int* ptr = static_cast<int*>(arg); // 更安全地将 void* 转换为 int*
if (ptr != nullptr) {
std::cout << "Hello, I am thread_" << *ptr << std::endl;
sleep(1); // 暂停 1 秒
}
// 返回一个全局或动态分配的整数,或者使用 pthread_exit 传递退出码
int* ret_ptr = new int(0); // 动态分配内存
return static_cast<void*>(ret_ptr); // 返回动态分配的整数的地址
}
int main()
{
int id = 0;
pthread_t tid;
// 创建线程
if (pthread_create(&tid, NULL, thread_Func, &id) != 0)
{
perror("pthread_create");
exit(1);
}
// 线程等待
void *ptr = nullptr;
if (pthread_join(tid, &ptr) != 0)
{
perror("pthread_join");
exit(1);
}
// 转换 void* 为 int* 并访问值
if (ptr != nullptr)
{
int* ret = static_cast<int*>(ptr);
std::cout << "Exit code is " << *ret << std::endl;
delete ret; // 释放之前动态分配的内存
}
return 0;
}
在这里一定要注意,不要返回栈上的临时变量,这是不安全的也指针访问!!!
4. 线程的分离
线程的等待是用于回收其他线程的资源,就像主线程是大哥大,其他线程就像小弟,被主线程派去干活,干的活结束还必须向大哥大(主线程)汇报情况。
但是线程分离后,小弟我要单干了!没有必要向你汇报了,分离的线程在结束时会自动释放其占用的资源,不需要主动回收。
相关函数是:int pthread_detach(pthread_t thread);
thread
:要分离的线程的标识符(ID
)。这个标识符是通过pthread_create
函数创建线程时返回的。- 返回值:成功时,返回 0。出错时,返回错误码。
举个栗子:
cpp
#include <cerrno>
#include <unistd.h>
#include <pthread.h>
#include <iostream>
#include <pthread.h>
#include <unistd.h> // 包含 sleep 函数的定义
void* thread_Func(void* arg)
{
int cnt = 0;
while(true)
{
std::cout << "I am doing my job, pthread_id is 1!" << std::endl;
if(cnt++ == 5)
{
break;
}
sleep(1);
}
return nullptr;
}
int main()
{
int id = 0;
pthread_t tid;
// 创建线程
if (pthread_create(&tid, NULL, thread_Func, &id) != 0)
{
perror("pthread_create");
exit(1);
}
// 线程分离
if(pthread_detach(tid) != 0)
{
perror("pthread_create");
exit(1);
}
int cnt = 0;
while(true)
{
std::cout << "I am doing my job, pthread_main!" << std::endl;
if(cnt++ == 5)
{
break;
}
sleep(1);
}
return 0;
}
这样的话,主线程就可以干自己的事情去啦!
5. 总结
这篇文中主要介绍了线程的基础控制,希望大家有所收获!