pthread 线程:
pthread 是 POSIX 线程 (POSIX Threads) 的缩写,提供了一组用于多线程编程的库函数。这些函数允许程序员在 C/C++ 代码中创建和管理多个并发执行的线程,从而可以在多核处理器上更高效地执行任务。使用线程要包含#include <pthread.h>。并且在编译是加上 -lpthread。
进程是系统中最小的资源分配单位。 线程是系统中最小的执行单位。
命令:ps -eLf (全部打印)或ps -eLo pid,ppid,lwp,stat,comm(部分打印)
基本概念
线程:线程是程序执行的基本单位,一个程序可以包含多个线程,每个线程执行不同的任务,但共享同一进程的地址空间。
主线程:程序启动后自动创建的第一个线程,通常是 main 函数执行的线程。
子线程:由主线程或其他线程创建的线程。
线程的优点: 比多进程节省资源,可以共享变量。
进程与线程的区别:
资源:线程比进程多了共享资源(线程存在竞争问题)。进程可以通过(IPC进程通信实现数据共享)、线程又具有部分私有资源。进程间只有私有资源没有共享资源。
空间:进程空间独立,不能直接通信。线程可以共享空间,可以直接通信。生成进程是要生成3G的内存的空间(code区、data区、堆区、共享区、栈区)。而线程只会生成栈的空间(8M),其它数据共享。在多线程中使用exit的话回结束子线程。只保留一个线程。
使用范围:进程适应于复杂任务,线程适用于简单重复性操作多的任务。
1、创建线程 pthread_create 函数
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);
参数:
thread:指向保存新线程 ID 的变量。
attr:线程属性,一般传递 NULL 表示默认属性。
start_routine:新线程执行的函数,这个函数的返回值是 void* 类型,参数也是 void* 类型。
arg:传递给 start_routine 的参数。
返回值:成功 0; 失败 错误码
注意:一次pthread_create执行只能创建一个线程。每个进程至少有一个线程称为主线程。主线程退出则所有创建的子线程都退出。 主线程必须有子线程同时运行才算多线程程序。 线程id是线程的唯一标识,是CPU维护的一组数字。pstree 查看系统中多线程的对应关系。多个子线程可以执行同一回调函数。
2、等待线程结束 pthread_join 函数
int pthread_join(pthread_t thread, void **retval);
参数:
thread:要等待的线程 ID。
retval:用于获取线程的返回值。
3、 终止线程 pthread_exit 函数与pthread_cancel函数
pthread_exit 和 pthread_cancel 是 pthread 库中用于控制线程终止的两个函数。
前者子线程自行退出,后者主线程请求结束一个线程。但是这两个函数都不会释放子进程栈的空间。
void pthread_exit(void *retval);
参数:retval:指定线程的返回值,该值会传递给 pthread_join 函数的 retval 参数。
pthread_exit 用于显式终止调用它的线程。调用这个函数会使得线程正常结束,并且可以传递一个返回值给调用 pthread_join 的线程。需要注意的是,调用 pthread_exit 不会终止整个进程,只会终止当前线程。
int pthread_cancel(pthread_t thread);
参数:thread:要取消的线程的 ID。
返回值:成功 0,失败 -1;
pthread_cancel 用于请求取消一个指定的线程。这个函数不会立即终止线程,而是发送一个取消请求。被取消的线程可以选择响应或忽略这个请求,具体取决于线程的取消状态和类型。
总结:
pthread_exit:用于线程自身主动退出。线程可以返回一个值供 pthread_join 处理。适用于正常终止线程的场景。
pthread_cancel:用于请求终止另一个线程。线程的终止取决于其取消状态和类型,可能不立即生效。适用于需要主动取消长时间运行或被卡住的线程。
4、线程回收
pthread_detach 和 pthread_join 都是用于管理线程生命周期的函数,它们的主要功能是处理线程的退出和资源回收,但它们的用途和使用方式不同。
int pthread_join(pthread_t thread, void **retval);
thread:要等待的线程的 ID。
retval:用于获取目标线程的返回值,如果不需要获取返回值可以传递 NULL。
pthread_join 函数用于等待一个线程的结束,并回收该线程的资源。调用 pthread_join 的线程将被阻塞,直到目标线程结束。
#include <stdio.h>
#include <pthread.h>
void* thread_func(void* arg) {
printf("Thread is running\n");
pthread_exit((void*) 0); // 线程正常退出
}
int main() {
pthread_t thread;
pthread_create(&thread, NULL, thread_func, NULL);
void* retval;
pthread_join(thread, &retval); // 等待线程退出,并获取返回值
printf("Thread exited with code %ld\n", (long)retval);
return 0;
}
int pthread_deatch() 函数
int pthread_detach(pthread_t thread);
参数:thread:要分离的线程的 ID。
函数用于将一个线程的状态设置为“分离状态(detached state)”。在分离状态下,线程结束时会自动释放其占用的资源,而不需要其他线程通过 pthread_join 来显式回收。该子线程结束后,操作系统释放该线程的资源, 需要通过主线程来释放PCB资源 。
#include <stdio.h>
#include <pthread.h>
void* thread_func(void* arg) {
printf("Detached thread running\n");
sleep(2); // 模拟一些工作
printf("Detached thread finished\n");
return NULL;
}
int main() {
pthread_t thread;
pthread_create(&thread, NULL, thread_func, NULL);
pthread_detach(thread); // 设置线程为分离状态
// 主线程继续执行
printf("Main thread finished\n");
sleep(3); // 给分离线程足够的时间完成
return 0;
}
5、线程清理函数
线程清理函数是在线程退出时自动调用的函数。它用于释放线程在运行过程中申请的资源,确保程序不会出现资源泄漏,在大型项目中子线程申请的资源可能会在别的函数里使用,所以不可能该子线程结束了就清除资源,应该在其它地方使用后,统一清除这些资源才用这2个函数清除资源pthread_cleanup_push 和 pthread_cleanup_pop。
pthread_cleanup_push()
这个函数用于注册一个清理函数,当线程通过调用 pthread_exit、响应 pthread_cancel 或者在线程中引发取消点时,注册的清理函数会被调用
void pthread_cleanup_push(void (*routine)(void *), void *arg);
routine:清理函数的指针。
arg:传递给清理函数的参数
pthread_cleanup_pop()
void pthread_cleanup_pop(int execute);
execute,非0 执行清理函数
0 ,不执行清理
这个函数用于调用清理函数,并且可以选择性地执行该清理函数。
#include <stdio.h>
#include <pthread.h>
void cleanup(void* arg) {
printf("Cleanup: %s\n", (char*)arg);
}
void* thread_func(void* arg) {
pthread_cleanup_push(cleanup, "Thread Exiting"); // 注册清理函数
printf("Thread is running\n");
sleep(2); // 模拟一些工作
// 使用 cleanup pop 来控制是否调用清理函数
pthread_cleanup_pop(1); // 传递 1,执行清理函数
return NULL;
}
int main() {
pthread_t thread;
pthread_create(&thread, NULL, thread_func, NULL);
pthread_join(thread, NULL); // 等待线程结束
return 0;
}
pthread_cleanup_push 和 pthread_cleanup_pop 必须成对出现,并且通常在同一代码块内使用。
当线程退出时,无论是正常退出还是由于取消请求而退出,pthread_cleanup_push 注册的清理函数都会被执行。
6、线程传值。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
typedef struct
{
char buf[50];
int num;
}ARG;
void* th(void* arg)
{
ARG* tmp = (ARG*)arg;
strcat(tmp->buf,"123456");
tmp->num +=10;
return tmp;
}
int main(int argc, char *argv[])
{
ARG arg={0};
bzero(&arg,sizeof(arg));
strcpy(arg.buf,"hello");
arg.num = 20;
pthread_t tid;
pthread_create(&tid,NULL,th,&arg);
void* ret=NULL;
pthread_join(tid,&ret);
printf("ret %s %d\n",((ARG*)ret)->buf,((ARG*)ret)->num);
//free(ret);
return 0;
}
这个程序通过创建一个线程来处理数据,然后主线程等待子线程完成并获取结果,最后输出处理后的数据。代码展示了基本的多线程操作和线程间数据共享的方式