线程
概念:
线程是轻量级进程,一般是一个进程中的多个任务。
进程是系统中最小的资源分配单位.
线程是系统中最小的执行单位。
特征:
1、共享资源
2、效率高 30%
3、三方库: pthread clone posix
3.1 编写代码头文件: pthread.h
3.2 编译代码加载库: -lpthread library
在编译使用 POSIX 线程(pthreads
)的程序时,需要链接 pthread
库。
gcc 1.c -lpthread //编译时,需要加-lpthread,因为编译线程,需要链接所需的 libpthread 库
缺点:
1,线程和进程相比,稳定性,稍微差些
2,线程的调试gdb,相对麻烦些。
线程与进程区别:
资源:
线程比进程多了共享资源。
线程又具有部分私有资源。
进程间只有私有资源没有共享资源。
空间:
进程空间独立,不能直接通信。
线程可以共享空间,可以直接通信。
线程的设计框架 (posix)
创建多线程 ==》线程空间操作 ===》线程资源回收
errno strerror(errno) perror();
创建多线程:
int pthread_create(pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine) (void *),
void *arg);
功能:该函数可以创建指定的一个线程。
参数:thread 线程id,需要实现定义并由该函数返回。
attr 线程属性,一般是NULL,表示默认属性。
start_routine 指向指针函数的函数指针。本质上是一个函数的名称即可。称为th 回调函数,是线程的执行空间。
arg 回调函数的参数,即参数3的指针函数参数。
返回值:
成功 0
失败 错误码
注意:
1.一次pthread_create执行只能创建一个线程。
2.每个进程至少有一个线程称为主线程。
3. 主线程退出则所有创建的子线程都退出。
4.主线程必须有子线程同时运行才算多线程程序。
5.线程id是线程的唯一标识,是CPU维护的一组数字。
pstree 查看系统中多线程的对应关系。
多个子线程可以执行同一回调函数。
ps -eLf 查看线程相关信息Low Weigth Process
ps -eLo pid,ppid,lwp,stat,comm
线程可以通过以下几种方式退出
正常退出:
线程执行完其函数体后,如果到达了函数的结尾,线程将正常退出。
void* thread_function(void* arg) {
// 线程的工作代码
return NULL; // 线程执行结束,返回NULL
}
使用 pthread_exit
显式退出:
线程可以通过调用 pthread_exit
函数来提前退出。这个函数需要一个指向退出状态的指针,这个状态可以被其他线程通过 pthread_join
获取。
#include <pthread.h>
void* thread_function(void* arg) {
// 线程的工作代码
pthread_exit(arg); // 使用传递给线程的参数作为退出状态
}
取消线程:
其他线程或主线程可以使用 pthread_cancel
函数来请求取消(终止)一个线程。被取消的线程将在其下一次检查取消点时退出,或者如果它正在执行可被取消的系统调用,则立即退出
#include <pthread.h>
void cancel_thread(pthread_t thread_id) {
pthread_cancel(thread_id); // 请求取消线程
}
线程的回收
1、线程的回收机制 ====》不同与进程没有孤儿线程和僵尸线程。
====》主线程结束任意生成的子线程都会结束。
====》子线程的结束不会影响主线程的运行。
pthread_join
原型:
int pthread_join(pthread_t thread, void **retval);
功能:
通过该函数可以将指定的线程资源回收,该函数具有阻塞等待功能,如果指定的线程没有结束,则回收线程会阻塞。
参数:
thread 要回收的子线程tid
retval 要回收的子线程返回值/状态。==》ptread_exit(值);
返回值:
成功 0
失败 -1;
子线程的回收策略:
1、如果预估子线程可以有限范围内结束则正常用pthread_join等待回收。
2、如果预估子线程可能休眠或者阻塞则等待一定时间后强制回收。
3、如果子线程已知必须长时间运行则,不再回收其资源。
线程的参数
传参数
线程的传参的类型为void **retval,在这里传的是**retval,这个是指针的指针,含义大多为改变指针的指向,对于函数传参来说,一个*代表传值,而**代表传一个指针的指向。
void* th(void* arg)
{
char * tmp = (char*)arg;//因为传过来的参数,是void *,所以在这里需要强转为char *
printf("tmp %s\n",tmp);
strcat(tmp,"123");
printf("tmp 2 %s\n",tmp);
return tmp;
}
int main(int argc, char *argv[])
{
char buf[128]="hello,world";
pthread_t tid;
pthread_create(&tid,NULL,th,buf);
void* ret=NULL;
pthread_join(tid,&ret);
printf("ret %s\n",(char*)ret);
//free(ret);
return 0;
}
但大多数来说,如果传入的参数不止一个,且类型不同的参数时,要用结构体来进行传参。在子线程接收时,也要进行强制类型转换,转换为结构体类型。代码如下:
#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);//主线程调用时,也需要强转一下。
return 0;
}
分离属性
指的是线程结束时系统会自动回收其资源,而不需要其他线程调用 pthread_join
来等待它结束。
pthread_deatch
原型:
int pthread_deatch(pthread_t thread);
功能:
设置分离属性
参数:
线程id号。
返回值说明:
线程分离成功返回0,失败返回错误码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
void *th1 (void* arg)
{
pthread_detach(pthread_self());//设立分离属性,将线程的回收交给系统
printf("th1 a is %lu\n",pthread_self());
pthread_exit(NULL);
}
int main(int argc, char *argv[])
{
pthread_t tid1;
int i = 0 ;
for(i = 0 ;i<55000;i++)
{
int ret = pthread_create(&tid1,NULL,th1,NULL);
if(0!=ret)
{
printf("i is %d\n",i);
break;
}
}
printf("i is %d\n",i);
return 0;
}
线程的清理
1.pthread_cleanup_push
void pthread_cleanup_push(void (*routine)(void *), void *arg);
功能:
注册一个线程清理函数
参数:
routine,线程清理函数的入口
arg,清理函数的参数。
返回值:无
2.pthread_cleanup_pop
void pthread_cleanup_pop(int execute);
功能:调用清理函数
参数:
execute,非0 执行清理函数
0 ,不执行清理
返回值:无
注意:在这里可以将函数清理理解为do while();pthread_cleanup_push可以理解为do , pthread_cleanup_pop 可以理解为判断条件while();他俩是成对出现的,有push,就必须有pop.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
void clean(void *arg)
{
printf("this is clean up ,%s\n",(char*)arg);
free(arg);
}
int main(int argc, char *argv[])
{
char * p =(char*)malloc(50);
pthread_cleanup_push(clean,p);
strcpy(p,"hello");
printf("main th %s\n",p);
pthread_cleanup_pop(1);
return 0;
}