Linux多线程(1)- 线程基础概念及AP介绍

1.线程基本介绍

进程是资源分配的基本单位
线程是进行CPU任务调度的基本单位

线程所必须的信息:线程ID、寄存器值、栈、调度优先级和策略、信号屏蔽字、errno变量、线程私有数据

多线程的好处:某些线程在阻塞时,还有另外一些线程可以运行,多线程程序可以改善响应时间和吞吐量
多线程的资源共享:一个进程的所有线程共享可执行程序的代码、程序的全局内存和堆内存、栈、文件描述符。

2.线程标识

线程标识:线程ID,pthread_t,在POSIX C的线程操作接口中,都是以pthread_t为单位
进程标识:进程ID,pid_t,标识进程

3.线程基本操作API

3.1 pthread_equal 线程比较

int pthread_equal(pthread_t tid1, pthread t tid2);
// 返回值:若相等,返回非0数值:否则,返回

3.2pthread_self 获得自身线程ID

pthread_t pthread_self(void);
// 返回值:调用线程的线程id

3.3 pthread_create 线程创建

int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr,
void (*start_rtn)(void *), void *restrict arg);
// tid:线程id,attr:线程属性,start_rtn:线程执行函数,arg:传入线程参数
// 返回值:若成功,返回0; 否则,返回错误编号
// 注:线程创建时,并不会保证是新创建的线程还是调用线程先运行。

3.4 pthread_exit线程终止

void pthread_exit(void *rval_ptr);
// rval_ptr 退出码,可以通过pthread_join访问到这个值

线程的三种退出方式(不退出进程):
(1)从启动例程中返回,返回值是线程退出码(即线程执行程序)
(2)线程可以被同一进程中其他线程取消
(3)线程调用pthread_exit

线程退出(会退出进程):
进程中任意线程调用exit 、_Exit、_exit 整个进程都会终止

3.5 pthread_join回收线程

int pthread_join(pthread t thread, void **rval_ptr);
// 返回值:若成功,返回0:否则,返回错误编号

调用线程将阻塞,直到指定线程返回,回收线程资源
(1)从线程执行程序返回,rval_ptr包含返回码,如果设置为NULL,就代表不获取返回值
(2)线程被取消,由rval_ptr指定的内存单元设置为PTHREAD_CANCELED
(3)rval_ptr也可以是复杂的结构体

3.6 phread_detach分离线程

int pthread_detach(pthread_t tid);
// 返回值:若成功,返回0;否则,返回错误编号

结合线程:pthread_join 结束线程,释放资源。
分离线程:线程退出时,自动释放资源。(不需要再pthread_join 会发生未知结果)
这里的释放资源是线程管理资源,如果是手动申请的内存,则需要手动去释放内存

3.6 线程取消与清理

3.6.1 pthread_cancle

int pthread_cancel(pthread_t tid);
// 返回值:若成功,返回0;否则,返回错误编号

发出cancle请求,直接终止线程而不等待。当线程运行到取消点(系统调用)时,则会被直接终止。

3.6.2 pthread_cleanup_push

void pthread_cleanup_push(void (*rtn)(void *), void *arg);
void pthread_cleanup pop(int execute) ; // execute为0,代表仅弹出函数而不执行 为1时代表弹出并执行

当线程执行以下动作时,会执行pthread_cleanup_push进去的线程清理函数,这个函数可以push进去多个,pop时按顺序弹出
1)调用pthread_exit
2)响应取消请求
3)用非零execute 参数调用pthread_cleanup_pop

3.6.3 pthread_cancle的陷阱

pthread_cancle非常容易引入问题,例如死锁、程序崩溃等问题。
image.png
假设线程A执行到sleep时,其他线程使用pthread_cancle将其强行退出,则已经上锁的这把锁,将永远处于上锁状态而无法释放,其他地方拿锁则会出现死锁问题。这里不光是死锁,一些共享资源,如申请的内存等,也会出现问题。

这里自己总结了几种方法处理这些问题
1)注册pthread_clean_up线程清理函数,在处理函数内释放锁、共享资源等(效率较高)
2)加锁前,使用pthread_setcancelstate,禁止被cancle,随后再重新允许cancle(需要等到一段代码执行完)

下面两个宏,分别是用方法1和方法2完成对锁的清理。

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#define USE_PTHREAD_CANCLESTATE
// #define USE_PTHREAD_CLEANUP

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void thread_report_sd_cleanup(void *argv) 
{
    printf("xzxxxxxxxxxxxx thread thread_report_sd_cleanup 111\n");
    pthread_mutex_t *lock = (pthread_mutex_t *)argv;
    pthread_mutex_unlock(lock);
    printf("xzxxxxxxxxxxxx thread thread_report_sd_cleanup 222\n");
}

void *thread_report_sd_property(void *argv) 
{      
    printf("xzxxxxxxxxxxxx thread sd 111\n");
#ifdef USE_PTHREAD_CANCLESTATE
    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
#endif

#ifdef USE_PTHREAD_CLEANUP
    pthread_cleanup_push(thread_report_sd_cleanup, &mutex);
#endif
    pthread_mutex_lock(&mutex);

    sleep(20);
    // pthread_exit(0);

    pthread_mutex_unlock(&mutex);

#ifdef USE_PTHREAD_CLEANUP
    pthread_cleanup_pop(0);
#endif

#ifdef USE_PTHREAD_CANCLESTATE
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
#endif
    printf("xzxxxxxxxxxxxx thread sd 222\n");

    // sleep(5);
    printf("xzxxxxxxxxxxxx thread sd 333\n");


    return NULL;
}

int main(int argc, const char** argv) {

    pthread_t sd_report_t = 0;
    pthread_create(&sd_report_t, NULL, thread_report_sd_property, NULL);


    sleep(1);

    int iRet = pthread_cancel(sd_report_t);
    printf("xzxxxxxxxxxxxx thread sd ret:%d\n", iRet);
    pthread_join(sd_report_t, NULL);
    printf("xzxxxxxxxxxxxx thread sd done ret:%d\n", iRet);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值