.webp)
Thread线程从零认识到深层理解
AI-摘要
Tianli GPT
AI初始化中...
介绍自己
生成本文简介
推荐相关文章
前往主页
前往tianli博客
线程
1. 线程概述
- 每一个进程【Process】都是由【线程 Thread 组成】,程序可以利用多线程方式来解决进程中的相关任务内容
- 线程使用的资源是由进程分配,不会自行申请资源内容,同时 CPU 可以指挥线程进行相关的任务执行。
- 进程中使用线程来完成多任务执行,可以降低系统资源的占用,但是线程对应进程而言不易过多,会导致线程资源冲突,性能降低。
- 线程项目中使用情况
- 文件管理一个线程
- 网络连接一个线程
- 进程通讯管理,利用一个线程
- .......
2. 进程和线程的内存问题
- 进程的内存依然是系统分配的: 栈区,堆区,BSS 区,数据区和代码区。
- 线程使用的内存是当前进程分配给线程使用的,每一个线程都在栈区使用一部分数据空间,作为自行执行任务,相关数据的必要内存空间,其他内存使用的都是当前进程中的相关资源。
- 因为线程都在当前进程中存在,线程与线程之间的通讯效率较高!同时存在共享资源冲突问题,后续需要利用 lock 机制解决。
- 进程和线程对比,线程占用的资源较少,进程占用的相关资源内容较多。

3. 线程基本操作
注意,编译 pthread 相关代码,格式案例如下
gcc 01-pthread_create.c -pthread
3.1 线程创建 pthread_create
创建一个线程,函数 pthread_create
函数文档:
#include <pthread.h> int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
函数功能:
- 在当前进程中,创建一个线程,用于执行目标 start_routine 函数对应功能
函数参数:
pthread_t *thread :
typdef unsigned long int pthread_t
当前创建的线程 id 号,需要一个变量地址,创建成功之后,会将对应的线程 ID 存储到当前变量中。const pthread_attr_t *attr :
线程属性共用体 union 类型,存储当前线程的相关属性 (attribute)void *(*start_routine) (void *) :
函数指针,是当前线程执行的任务函数,也可以认为是开始函数,what will be run?。函数指针对应类型要求,函数参数类型为 (void *),函数返回值类型也是 void *void * arg :
提供给当前函数指针指向函数执行必要参数返回值
如果函数调用成功,
pthread_create
会返回0
。如果调用失败,则会返回一个非零的错误码,用于指示具体的错误类型。常见的错误码有:
EAGAIN
:系统资源不足,无法创建新线程。EINVAL
:传递的attr
参数无效。EPERM
:没有足够的权限来设置指定的线程属性。
线程基本创建
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
/*
#include <pthread.h>
int pthread_create(pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine) (void *),
void *arg);
pthread_t pthread_self (void)
获取当前线程 ID 号、线程标识符
*/
void *thread_fun1(void *arg);
void *thread_fun2(void *arg);
int main(int argc, char const *argv[])
{
pthread_t thread_id1 = 0;
pthread_t thread_id2 = 0;
pthread_create(&thread_id1, NULL, thread_fun1, NULL);
pthread_create(&thread_id2, NULL, thread_fun2, NULL);
sleep(15);
return 0;
}
void *thread_fun1(void *arg)
{
int second = 10;
while (second > 0)
{
printf("PID : %d, thread_id : %ld, Thread Fun1 Running! Thread Exit int %d second!\n",
getpid(), pthread_self(), second);
second -= 1;
sleep(1);
}
return NULL;
}
void *thread_fun2(void *arg)
{
int second = 10;
while (second > 0)
{
printf("PID : %d, thread_id : %ld, Thread Fun2 Running! Thread Exit int %d second!\n",
getpid(), pthread_self(), second);
second -= 1;
sleep(1);
}
return NULL;
}
验证线程执行执行函数所需参数情况
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
/*
#include <pthread.h>
int pthread_create(pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine) (void *),
void *arg);
pthread_t pthread_self (void)
获取当前线程 ID 号、线程标识符
*/
void *thread_fun1(void *arg);
void *thread_fun2(void *arg);
int main(int argc, char const *argv[])
{
pthread_t thread_id1 = 0;
pthread_t thread_id2 = 0;
int num = 100;
/*
利用 pthread_create 函数第四个参数位置,将 num 作为执行所需函数
对应的参数内容
校验四种形式
1. num ERROR
2. &num SUCCESS
3. (void *)&num SUCCESS
4. (void *)num ERROR
执行线程操作之后,num的存储数据为
1. 100
2. 200
3. 500
200 和 500 随机情况,看哪一个线程后触发执行。
thread_id1 和 thread_id2 线程,执行了多少次线程任务?
A. 1 B. 10 C. 2 D. 钝角
每一个线程中的线程函数,各自有且只执行一次,代码中出现的
循环效果是函数内部 while 循环导致的。
*/
pthread_create(&thread_id1, NULL, thread_fun1, &num);
pthread_create(&thread_id2, NULL, thread_fun2, (void *)&num);
sleep(15);
printf("num = %d\n", num);
return 0;
}
void *thread_fun1(void *arg)
{
int second = 10;
/*
*arg = 200; ERROR
因为 arg 数据类型为 void *,进行取值操作之后,对应的真实类型
为 void 类型,void 类型无法进行任何的赋值操作和取值操作。计
算机认为是空类型
*((int *)arg) = 200; SUCCESS
首先对 arg 进行强制类型转换,将原本 void * 指针转换为 int * 指针
再进行数据的取值赋值操作
*/
*((int *)arg) = 200;
while (second > 0)
{
printf("PID : %d, thread_id : %ld, Thread Fun1 Running! Thread Exit int %d second!\n",
getpid(), pthread_self(), second);
second -= 1;
sleep(1);
}
return NULL;
}
void *thread_fun2(void *arg)
{
int second = 10;
*((int *)arg) = 500;
while (second > 0)
{
printf("PID : %d, thread_id : %ld, Thread Fun2 Running! Thread Exit int %d second!\n",
getpid(), pthread_self(), second);
second -= 1;
sleep(1);
}
return NULL;
}
3.2 线程等待 pthread_join
函数文档
#include <pthread.h> int pthread_join(pthread_t thread, void **retval);
- 函数功能:
- tips: retval ==> return value 返回值,因为 pthread 线程执行目标任务函数的返回值为
void *,当前 retval 存储返回值数据的地址。- 等待子线程运行结束,并且回收子线程使用的相关资源。
- 函数参数:
pthread_t thread :
对应执行 pthread_join 函数的目标线程 thread id 数据void **retval:
线程执行完成之后,线程执行函数的返回值存储到retval
中,后续可以对应当前函数的返回值内容,进行相关的资源处理。 因为pthrea_create
函数中目标函数返回值类型为void *
,当前存储目标类型是一个void *
类型,参数类型是一个二级指针void **
,如不需要接受返回值,参数可以是NULL
- 返回值
- 成功返回 0
- 失败返回非 0
pthread_join 阻塞效果
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
void *thread_fun(void *arg);
int main(int argc, char const *argv[])
{
pthread_t thread_id1 = 0;
int ret = pthread_create(&thread_id1, NULL, thread_fun, NULL);
if (ret)
{
perror("pthread_create failed");
}
printf("Before Join!\n");
/*
一旦执行 pthread_join 函数,当前 main线程/进程 被阻塞,优先保证 thread_id1
对应线程执行。等待目标子线程执行完毕
*/
pthread_join(thread_id1, NULL);
int n = 10;
while (n > 0)
{
printf("Process Running!\n");
sleep(1);
n -= 1;
}
printf("After Join!\n");
return 0;
}
void *thread_fun(void *arg)
{
int second = 10;
while (second > 0)
{
printf("PID : %d, thread_id : %ld, Thread Fun1 Running! Thread Exit int %d second!\n",
getpid(), pthread_self(), second);
second -= 1;
sleep(1);
}
return NULL;
}
pthread_join 利用第二个参数获取线程执行函数返回值数据

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
void *thread_malloc(void *arg);
int main(int argc, char const *argv[])
{
pthread_t thread_id1 = 0;
int buf_size = 64;
int ret = pthread_create(&thread_id1, NULL, thread_malloc, &buf_size);
if (ret)
{
perror("pthread_create failed");
}
void * retval = NULL;
/*
将子线程执行函数的返回值,存储到 retval 中
*/
pthread_join(thread_id1, &retval);
for (int i = 0; i < buf_size / sizeof(int); i++)
{
printf("retval data : %d\n", ((int *)retval)[i]);
}
free(retval);
return 0;
}
void *thread_malloc(void *arg)
{
// 获取用户提供给当前线程执行函数的参数内容
int buf_size = *((int *)arg);
// 如果提供的 buf_size 小于等于 0,直接返回 NULL
if (buf_size <= 0)
{
return NULL;
}
void * buffer = malloc(buf_size);
memset(buffer, 0, buf_size);
for (int i = 0; i < buf_size / sizeof(int); i++)
{
((int *)buffer)[i] = i + 1;
}
return buffer; // void * buffer ==> 0x1000;
}
3.3 线程分离 pthread_detach
pthread_join 之后,主线程会进入阻塞状态,可以使用 pthread_detach 函数将子线程和主线程进行分离,作为独立线程执行
函数文档
#include <pthread.h> int pthread_detach(pthread_t thread);
- 函数功能:
- 根据指定的线程 ID 号,将对应的线程分离为独立的线程,尤其是在 join 函数执行之前,可以避免线程阻塞
- 函数参数:
pthread_t thread :
目标分离的线程 pthread_t 线程 ID/线程标识符确定哪一个线程进行分离- 返回值类型:
- 操作成功,返回 0
- 操作失败,返回非 0 值
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
void *thread_function_1(void *arg);
int main(int argc, char const *argv[])
{
pthread_t thread_id = 0;
int count = 20;
int ret = pthread_create(&thread_id, NULL, thread_function_1, NULL);
if (ret)
{
perror("pthread_create failed");
}
pthread_detach(thread_id);
pthread_join(thread_id, NULL);
int second = 2;
while (second > 0)
{
printf("Thread ID : %ld, main thread runnning!!!\n", pthread_self());
second -= 1;
sleep(1);
}
return 0;
}
void *thread_function_1(void *arg)
{
int second = 10;
while (second > 0)
{
printf("Thread ID : %ld, son thread runnning!!!\n", pthread_self());
second -= 1;
sleep(1);
}
return NULL;
}
3.4 线程退出 pthread_exit
exit(int status)
和_exit(int status)
,exit
是 C 库函数,_exit
是系统调用 System Calls 函数,作用都是退出当前进程。在多线程情况下,使用 exit 退出,会导致整个程序运行结束。
**函数文档 **
#include <pthread.h> void pthread_exit(void *retval);
- 函数功能:
- 退出当前执行的线程,同时清理当前线程使用的相关资源,另外函数参数是一个 (void * retval),可以将线程的退出状态告知外部。
- 函数参数:
void * retval :
当前线程的退出状态,可以在外边使用 pthread_join 来获取线程执行完毕之后的数据反馈
3.5 线程取消 pthread_cancel
函数文档
#include <pthread.h> int pthread_cancel(pthread_t thread);
- 函数功能:
- **可以发送给指定线程一个取消【信号】,**收到当前信号的线程, 会根据自身状态,选择是否取消。
- 函数参数:
pthread_t thread :
取消操作目标线程标识符- 返回值
- 成功返回 0,失败返回非 0 错误值。
线程取消情况说明
- 默认情况下,线程的取消状态是:【可以取消】【收到信号,到达取消点函数取消】
- 线程取消状态设置函数
int pthread_setcancelstate (int __state, int *__oldstate);
- 设置当前线程的取消状态
** 第一个参数 int _state 有两个系统预设的宏作为参数,分别为**
** PTHREAD_CANCEL_ENABLE 当前线程可以取消**
** PTHREAD_CANCEL_DISABLE 当前线程不可以取消**- 如果设置 PTHREAD_CANCEL_DISABLE 状态,其他线程发送给当前线程的取消信号会被挂起,直到当前线程恢复可退出状态,触发原本的取消信号。
- 线程取消类型设置函数
int pthread_setcanceltype(int type, int *oldtype);
- 设置当前线程的取消类型,有立即取消和到达取消点取消
PTHREAD_CANCEL_DEFERRED
【默认状态】当前线程收到取消信号,会执行到下一个取消点函数进行线程取消,较为安全。PTHREAD_CANCEL_ASYNCHRONOUS
收到取消信号立即取消,有一定的风险
- 感谢你赐予我前进的力量
赞赏者名单
因为你们的支持让我意识到写文章的价值🙏
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载必须注明来自 卡卡罗特
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果