Linux 线程

线程

概念:

线程是轻量级进程,一般是一个进程中的多个任务。
进程是系统中最小的资源分配单位.
线程是系统中最小的执行单位。

特征:


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;
}

  • 22
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值