目录
3.pthread_cleanup_push&pthread_cleanup_pop
线程的概念
会话是用来承载进程组的,里面可以有一个或多个进程,一个线程中可以有一个或多个线程
线程的本质就是一个正在运行的函数 ,线程没有主次之分(main函数 也只是一个main线程),多个线程之间共享内存,线程的调度取决于调度器的测略
posix线程是一套标准,而不是实现,我们主要讨论这套标准,线程标识 pthead_t 类型不确定
线程的创建和终止
创建线程
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
成功时返回 0。失败时返回错误码。
pthread_t *thread: 指向 pthread_t 类型的指针,用于存储新创建的线程的标识符。
const pthread_attr_t *attr: 指向线程属性对象的指针,可以指定线程的属性,如栈大小、调度策略等。如果不需要指定特定的属性,可以传递 NULL。
void *(*start_routine) (void *): 线程函数的入口点,即线程执行的函数。这个函数接受一个 void* 类型的参数,并返回一个 void* 类型的值。
void *arg: 传递给线程函数的参数。
线程终止
线程从启动例程返回,返回值就是线程的退出码,线程可以被同一进程的其他线程取消,线程调用`pthread_exit()`函数
1.pthread_exit
void pthread_exit(void *retval);
结束自己的执行并将返回值retval传递给等待该线程结束的线程
2. pthread_join
int pthread_join(pthread_t thread, void **retval);
调用此函数的线程将阻塞,直到指定的线程终止。一旦被等待的线程终止,如果需要获取线程的返回值,则传入一个非 NULL 的指针。*retval指针将被设置为指向线程的返回值。
3.pthread_cleanup_push&pthread_cleanup_pop
void pthread_cleanup_push(void (*routine)(void *), void *arg);
void pthread_cleanup_pop(int execute); //0不执行1执行
类似钩子函数,程序只要正常终止,钩子函数就会被逆序调用,push设置钩子函数和参数,pop决定是否执行。因为这两个函数其实是宏,有push必须有pop
线程的取消
多线程任务 有时需要取消部分任务(线程)
取消有2种状态:
- 不允许
- 允许
- 异步cancel
- 推迟cancel(默认) 推迟到cancel点再响应
- cancel点 : POSIX定义的cancel点,都是可能引发阻塞的系统调用
1.pthread_cancel
int pthread_cancel(pthread_t thread);
请求取消指定的线程,成功时返回 0。失败时返回错误码。
2. pthread_setcancelstate
int pthread_setcancelstate(int state, int *oldstate);
用于控制线程的取消状态,PTHREAD_CANCEL_ENABLE: 允许取消请求。PTHREAD_CANCEL_DISABLE: 禁止取消请求。成功时返回 0。失败时返回错误码。
3. pthread_setcanceltype
int pthread_setcanceltype(int type, int *oldtype);
用于控制线程的取消状态和类型,PTHREAD_CANCEL_DEFERRED: 取消请求被推迟,直到线程退出不能被取消的系统调用。PTHREAD_CANCEL_ASYNCHRONOUS: 取消请求可以立即被处理。
int pthread_detach(pthread_t thread);
4.pthread_testcancel
void pthread_testcancel(void);
什么都不做,就是原地设置一个cancel点
5. pthread_detach
int pthread_detach(pthread_t thread);
将一个线程设置为分离状态(detached state)。在分离状态下,线程结束时不会等待其他线程来回收其资源,系统会自动回收这些资源。
线程同步
实现线程同步的互斥锁,pthread_mutex_t类型的互斥量锁住的是一段代码而不是一个变量。
1.pthread_mutex_init
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
初始化一个互斥锁,const pthread_mutexattr_t *attr: 指向互斥锁属性的指针。如果不需要特殊属性,可以传递 NULL。成功时返回 0。失败时返回错误码。
2. pthread_mutex_destroy
int pthread_mutex_destroy(pthread_mutex_t *mutex);
销毁一个互斥锁。
3. pthread_mutex_lock
int pthread_mutex_lock(pthread_mutex_t *mutex);
堵塞锁定一个互斥锁,成功时返回 0。失败时返回错误码。
4. pthread_mutex_trylock
int pthread_mutex_trylock(pthread_mutex_t *mutex);
尝试锁定一个互斥锁,但不阻塞。如果互斥锁成功被锁定,返回 0。如果互斥锁已经被其他线程锁定,返回 EBUSY。其他错误时返回相应的错误码。
5. pthread_mutex_unlock
int pthread_mutex_unlock(pthread_mutex_t *mutex);
解锁一个互斥锁。只有当前拥有互斥锁的线程才能调用此函数解锁。如果调用线程不是互斥锁的所有者,则会返回 EPERM 错误。
6. pthread_once
int pthread_once(pthread_once_t *once_control, void (*init_routine)(void));
通常用于全局变量的初始化,确保在多线程环境中,全局变量的初始化不会被多次执行
pthread_once_t *once_control: 指向 pthread_once_t 类型的变量,该变量用于控制初始化函数的执行。这个变量必须在调用 pthread_once() 之前被初始化为 PTHREAD_ONCE_INIT。
void (*init_routine)(void): 指向初始化函数的指针。这个函数将在第一次调用 pthread_once() 时执行,并且只会执行一次。
条件变量(pthread_cond_t类型)
1.pthread_cond_init
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
初始化一个条件变量,pthread_cond_t *cond: 指向条件变量对象的指针。const pthread_condattr_t *attr: 指向条件变量属性的指针。如果不需要特殊属性,可以传递 NULL。
2. pthread_cond_destroy
int pthread_cond_destroy(pthread_cond_t *cond);
销毁一个条件变量。
3.pthread_cond_wait
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
函数一进入wait状态就会自动release mutex。当其他线程通过pthread_cond_signal()或pthread_cond_broadcast,把该线程唤醒,使pthread_cond_wait()通过(返回)时,该线程又自动获得该mutex。
4. pthread_cond_broadcast
int pthread_cond_broadcast(pthread_cond_t *cond);
唤醒所有等待指定条件变量的线程。
5. pthread_cond_signal
int pthread_cond_signal(pthread_cond_t *cond);
唤醒一个等待指定条件变量的线程。
6. pthread_cond_timedwait
int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
允许指定一个超时时间,这样线程就不会无限期地等待条件变量。如果条件变量在指定的时间内被触发,线程会像 pthread_cond_wait 一样继续执行;如果超时时间到达,线程会恢复执行.
线程属性
int pthread_attr_init(pthread_attr_t *attr);
//初始化线程的属性对象,设置其为默认值。
int pthread_attr_destroy(pthread_attr_t *attr);
//销毁线程属性对象,释放与其关联的资源。
int pthread_attr_setaffinity_np(pthread_attr_t *attr, size_t cpusetsize, const cpu_set_t *cpuset); // 设置线程的CPU亲和性,指定线程可以在哪些CPU上运行。
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); // 设置线程的分离状态,决定线程是否应该被创建为分离态。
int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize); // 设置线程栈的保护区域大小,用于防止栈溢出。
int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched); // 设置线程的调度继承属性,决定新线程是否继承创建它的线程的调度属性。
int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param); // 设置线程的调度参数,如优先级等。
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy); // 设置线程的调度策略,如轮转调度等。
int pthread_attr_setscope(pthread_attr_t *attr, int scope); // 设置线程的并发范围,如系统范围或进程范围。
int pthread_attr_setsigmask_np(pthread_attr_t *attr, const sigset_t *sigmask); // 设置线程的信号掩码,决定线程忽略哪些信号。
int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize); // 设置线程栈的属性,如栈的大小和位置。
int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr); // 设置线程栈的起始地址。
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize); // 设置线程栈的大小。
int pthread_getattr_np(pthread_t thread, pthread_attr_t *attr); // 获取线程的属性。
int pthread_setattr_default_np(pthread_attr_t *attr); // 设置默认的线程属性。
互斥量属性
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
int pthread_mutexattr_init(pthread_mutexattr_t *attr);
//同上
int pthread_mutexattr_getpshared(const pthread_mutexattr_t *attr, int *pshared); // 获取互斥锁属性,确定互斥锁是否可以被不同进程中的线程共享。
int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared); // 设置互斥锁属性,指定互斥锁是否可以被不同进程中的线程共享。
int pthread_mutexattr_getpshared(const pthread_mutexattr_t *attr, int *pshared); // 获取互斥锁属性,此属性决定互斥锁是否可以跨进程共享。
int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared); // 设置互斥锁属性,允许指定互斥锁是否可以跨进程共享。
int pthread_mutexattr_gettype(const pthread_mutexattr_t *restrict attr, int *restrict type); // 获取互斥锁属性中的类型字段,该字段定义了互斥锁的类型。
int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type); // 设置互斥锁的类型,如正常互斥锁、错误检测互斥锁、递归互斥锁等。
条件变量属性
int pthread_condattr_destroy(pthread_condattr_t *attr); // 销毁条件变量属性对象,释放与其关联的资源。
int pthread_condattr_init(pthread_condattr_t *attr); // 初始化条件变量属性对象,设置其为默认值。
重入与设置屏蔽
多线程中的IO,IO函数支持多线程因为会加锁解锁缓冲区
- getchar_unlocked
int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset);
该函数用于设置调用线程的信号屏蔽字(mask),并可选地获取之前的屏蔽字。
- int how: 指定如何设置信号屏蔽字,可以是以下几种操作之一:
- SIG_BLOCK: 将当前屏蔽字与参数 set 指定的信号集进行按位或操作,添加信号到屏蔽字中。
- SIG_UNBLOCK: 将当前屏蔽字与参数 set 指定的信号集的补集进行按位与操作,从屏蔽字中移除信号。
- SIG_SETMASK: 直接用参数 set 指定的信号集替换当前的信号屏蔽字。
- const sigset_t *set: 指向新的信号集的指针,该信号集将根据 how 参数的值影响当前的信号屏蔽字。
- sigset_t *oldset: 指向一个信号集的指针,用于存储调用前线程的信号屏蔽字。如果不需要这个信息,可以传递 NULL。
int pthread_kill(pthread_t thread, int sig);
该函数用于向指定的线程发送信号。函数是线程安全的信号发送方法,它允许在多线程程序中向特定线程发送信号,而不是向整个进程发送信号。这与全局的 `kill` 函数不同,后者向进程发送信号,而不管信号在哪个线程中被捕捉或处理。
多线程实现令牌桶
main.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <math.h>
#include "mytbf.h"
static const int SIZE = 1024;
static const int CPS = 10;
static const int BURST = 100;//最大令牌数
static volatile int token = 0;//持有令牌数
int main(int argc,char** argv)
{
if (argc < 2){
fprintf(stdout,"Usage...");
exit(1);
}
mytbf_t *tbf;
tbf = mytbf_init(CPS,BURST);
if (tbf == NULL){
fprintf(stderr,"tbf init error");
exit(1);
}
//打开文件
int sfd,dfd = 0;
do{
sfd = open(argv[1],O_RDONLY);
if (sfd < 0){
if (errno == EINTR)
continue;
fprintf(stderr,"%s\n",strerror(errno));
exit(1);
}
}while(sfd < 0);
char buf[SIZE];
while(1){
int len,ret,pos = 0;
int size = mytbf_fetchtoken(tbf,SIZE);
//int i = 0;
//while(i < 2){
// sleep(1);
// i++;
//}
if (size < 0){
fprintf(stderr,"mytbf_fetchtoken()%s\n",strerror(-size));
exit(1);
}
len = read(sfd,buf,size);
while (len < 0){
if (errno == EINTR)
continue;
strerror(errno);
break;
}
//读取结束
if (len == 0){
break;
}
//要是读到结尾没用完token
if (size - len > 0){
mytbf_returntoken(tbf,size-len);
}
//以防写入不足
while(len > 0){
ret = write(dfd,buf+pos,len);
while (ret < 0){
if (errno == EINTR){
continue;
}
printf("%s\n",strerror(errno));
exit(1);
}
pos += ret;
len -= ret;
}
}
close(sfd);
mytbf_destroy(tbf);
exit(0);
}
mytbf.c
#include <errno.h>
#include <sched.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include <sys/time.h>
#include <pthread.h>
#include <string.h>
#include "mytbf.h"
struct mytbf_st{
int csp;
int burst;
int token;
int pos;//任务列表的下标
pthread_mutex_t mut;
pthread_cond_t cond;
};
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_t ptid;
static pthread_once_t pth_once = PTHREAD_ONCE_INIT;
static struct mytbf_st *job[MYTBF_MAX];
static volatile int inited = 0;
static int get_free_pos_unlocked(){
for (int i = 0;i < MYTBF_MAX;i++){
if (job[i] == NULL)
return i;
}
return -1;
}
//线程处理函数
static void *handler(void *p){
struct timespec ts;
ts.tv_sec = 1;
ts.tv_nsec = 0;
while(1){
pthread_mutex_lock(&mutex);
for (int i = 0;i < MYTBF_MAX;i++){
if (job[i] != NULL){
pthread_mutex_lock(&job[i]->mut);
job[i]->token += job[i]->csp;
if (job[i]->token > job[i]->burst){
job[i]->token = job[i]->burst;
}
pthread_cond_broadcast(&job[i]->cond);
pthread_mutex_unlock(&job[i]->mut);
}
}
pthread_mutex_unlock(&mutex);
nanosleep(&ts,NULL);
}
pthread_exit(NULL);
}
//卸载线程处理模块
static void mod_unload(){
pthread_cancel(ptid);
pthread_join(ptid,NULL);
for (int i = 0;i < MYTBF_MAX;i++){
if (job[i] != NULL){
mytbf_destroy(job[i]);
}
free(job[i]);
}
pthread_mutex_destroy(&mutex);
}
//装载线程处理模块
static void mod_load(){
int err = pthread_create(&ptid,NULL,handler,NULL);
if (err){
fprintf(stderr,"%s\n",strerror(err));
}
atexit(mod_unload);
}
mytbf_t *mytbf_init(int cps,int burst){
struct mytbf_st *tbf;
pthread_once(&pth_once,mod_load);
tbf = malloc(sizeof(*tbf));
if (tbf == NULL){
return NULL;
}
tbf->token = 0;
tbf->csp = cps;
tbf->burst = burst;
pthread_mutex_init(&tbf->mut,NULL);
pthread_cond_init(&tbf->cond,NULL);
pthread_mutex_lock(&mutex);
//将新的tbf装载到任务组中
int pos = get_free_pos_unlocked();
if (pos == -1){
free(tbf);
pthread_mutex_unlock(&mutex);
return NULL;
}
tbf->pos = pos;
job[pos] = tbf;
pthread_mutex_unlock(&mutex);
return tbf;
}
//获取token ptr是一个 void * size是用户想要获取的token数
int mytbf_fetchtoken(mytbf_t *ptr,int size){
struct mytbf_st *tbf = ptr;
if (size <= 0){
return -EINVAL;
}
//有token继续
pthread_mutex_lock(&tbf->mut);
while (tbf->token <= 0){
pthread_cond_wait(&tbf->cond,&tbf->mut);//等通知 抢锁
}
int n =tbf->token<size?tbf->token:size;
tbf->token -= n;
pthread_mutex_unlock(&tbf->mut);
//用户获取了 n 个token
return n;
}
//归还token ptr是一个 void *
int mytbf_returntoken(mytbf_t *ptr,int size){
struct mytbf_st *tbf = ptr;
if (size <= 0){
return -EINVAL;
}
pthread_mutex_lock(&tbf->mut);
tbf->token += size;
if (tbf->token > tbf->burst)
tbf->token = tbf->burst;
pthread_cond_broadcast(&tbf->cond);
pthread_mutex_unlock(&tbf->mut);
return size;
}
int mytbf_destroy(mytbf_t *ptr){
struct mytbf_st *tbf = ptr;
pthread_mutex_lock(&mutex);
job[tbf->pos] = NULL;
pthread_mutex_unlock(&mutex);
pthread_mutex_destroy(&tbf->mut);
pthread_cond_destroy(&tbf->cond);
free(tbf);
return 0;
}
mytbf.h
#ifndef MYTBF_H__
#define MYTBF_H__
#define MYTBF_MAX 1024
typedef void mytbf_t;
mytbf_t *mytbf_init(int cps,int burst);
//获取token
int mytbf_fetchtoken(mytbf_t *,int);
//归还token
int mytbf_returntoken(mytbf_t *,int);
int mytbf_destroy(mytbf_t *);
#endif
多线程实现读写管道
pipe.c
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#include "mypipe.h"
struct mypipe_st{
int head;
int tail;
char data[PIPESIZE];
int datasize;
int count_reader;
int count_writer;
pthread_mutex_t mutex;
pthread_cond_t cond;
};
mypipe_t *mypipe_init(){
struct mypipe_st *pipe;
pipe = malloc(sizeof(*pipe));
if (pipe == NULL){
return NULL;
}
//pipe结构初始化
pipe->head = 0;
pipe->tail = 0;
pipe->datasize = 0;
pthread_mutex_init(&pipe->mutex,NULL);
pthread_cond_init(&pipe->cond,NULL);
return pipe;
}
int mypipe_register(mypipe_t *ptr,int opmap){
struct mypipe_st *pipe = ptr;
pthread_mutex_lock(&pipe->mutex);
if(opmap & PIPE_READER){
pipe->count_reader++;
}
if(opmap & PIPE_WRITER){
pipe->count_writer++;
}
//读写双方不全
while(pipe->count_reader <= 0 || pipe->count_writer <= 0){
pthread_cond_wait(&pipe->cond,&pipe->mutex);
}
pthread_cond_broadcast(&pipe->cond);//读写双方凑齐
pthread_mutex_unlock(&pipe->mutex);
return 0;
}
int mypipe_unregister(mypipe_t *ptr,int opmap){
struct mypipe_st *pipe = ptr;
pthread_mutex_lock(&pipe->mutex);
if(opmap & PIPE_READER){
pipe->count_reader--;
}
if(opmap & PIPE_WRITER){
pipe->count_writer--;
}
//唤醒其他管道读写方检查读写者的数量
pthread_cond_broadcast(&pipe->cond);
pthread_mutex_unlock(&pipe->mutex);
return 0;
}
static int mypipe_readbyte_unlocked(struct mypipe_st *pipe,char *data){
//管道无数据
if (pipe->datasize <= 0){
return -1;
}
//管道有数据 读取一个现在管道的读端数据,用data保存
*data = pipe->data[pipe->head];
pipe->head = (pipe->head++)%PIPESIZE;
pipe->datasize--;
return 0;
}
static int mypipe_writebyte_unlocked(struct mypipe_st *pipe,const char *data){
//管道数据满
if (pipe->datasize >= PIPESIZE){
return -1;
}
//管道有数据 读取一个现在管道的读端数据,用data保存
pipe->data[pipe->tail+1] = *data;
pipe->tail = (pipe->tail++)%PIPESIZE;
pipe->datasize++;
return 0;
}
int mypipe_read(mypipe_t *ptr,void *buf,size_t size){
struct mypipe_st *pipe = ptr;
pthread_mutex_lock(&pipe->mutex);
while(pipe->datasize <= 0 && pipe->count_writer > 0){
pthread_cond_wait(&pipe->cond,&pipe->mutex);
}
//管道空且没有写者
if (pipe->datasize <= 0 && pipe->count_writer <= 0){
pthread_mutex_unlock(&pipe->mutex);
return 0;
}
//管道中有数据了
for (int i = 0;i < size;i++){
if (mypipe_readbyte_unlocked(pipe,buf+i) < 0){
break;
}
}
pthread_mutex_unlock(&pipe->mutex);
return 0;
}
int mypipe_write(mypipe_t *ptr,const void *buf,size_t size){
struct mypipe_st *pipe = ptr;
pthread_mutex_lock(&pipe->mutex);
while(pipe->datasize >= PIPESIZE && pipe->count_reader > 0){
pthread_cond_wait(&pipe->cond,&pipe->mutex);
}
//管道空且没有读者
if (pipe->datasize <= 0 && pipe->count_reader <= 0){
pthread_mutex_unlock(&pipe->mutex);
return 0;
}
//管道中有空间了
for (int i = 0;i < size;i++){
if (mypipe_writebyte_unlocked(pipe,buf+i) < 0){
break;
}
}
pthread_mutex_unlock(&pipe->mutex);
return 0;
}
int mypipe_destory(mypipe_t *ptr){
struct mypipe_st *pipe = ptr;
pthread_mutex_destroy(&pipe->mutex);
pthread_cond_destroy(&pipe->cond);
free(pipe);
return 0;
}
pipe.h
#ifndef MYPIPE_H__
#define MYPIPE_H__
#include <stdio.h>
#define PIPESIZE 1024
#define PIPE_READER 0x00000001UL //读者
#define PIPE_WRITER 0x00000002UL //写者
typedef void mypipe_t;
mypipe_t *mypipe_init();
//读者 写者 注册身份
int mypipe_register(mypipe_t *,int opmap);;
//读者 写者 注销身份
int mypipe_unregister(mypipe_t *,int opmap);
int mypipe_read(mypipe_t *,void *buf,size_t size);
int mypipe_write(mypipe_t *,const void *buf,size_t size);
int mypipe_destory(mypipe_t *);
#endif