线程的概念
- 会话是用来承载进程组的,里面可以有一个或多个进程,一个线程中可以有一个或多个线程
- 线程的本质就是一个正在运行的函数 ,线程没有主次之分,多个线程之间共享内存
- 线程的调度取决于调度器策略
- posix线程是一套标准,而不是实现,我们主要讨论这套标准
- 线程标识 pthead_t 类型不确定
线程的创建
pthread_equal 比较两个线程id,相同返回0
pthread_self 获取当前线程id
pthread_create 创建线程
int pthread_equal(pthread_t t1, pthread_t t2);
pthread_t pthread_self(void);
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
成功返回0,失败返回errno,
static void *func(void *p){
puts(“thread is working”);
return NULL;
}
pthread_t ptid;
pthread_create(&ptid,NULL,func,NULL);
线程的终止
线程从启动例程返回,返回值就是线程的退出码
线程可以被同一进程的其他线程取消
线程调用pthread_exit()函数
pthread_exit
pthread_join 先当于进程的 wait(),等待创建的线程运行完
void pthread_exit(void *retval);
int pthread_join(pthread_t thread, void **retval);
static void *func(void *p){
puts("thread is working");
pthread_exit(NULL);
}
pthread_create(&ptid,NULL,func,NULL);
pthread_join(ptid,NULL);
printf("End\n");
线程/栈的清理
- pthread_cleanup_push
- pthread_cleanup_pop
void pthread_cleanup_push(void (*routine)(void *), void *arg);
void pthread_cleanup_pop(int execute);
宏,成对出现,否则会报错
类似钩子函数,程序只要正常终止,钩子函数就会被逆序调用,push 与 pop 可以指定操作
pop参数填0时,只弹栈,不调用
static void cleanup(void *p){
puts(p);
}
static void *func(void *p){
puts("Thread is working");
pthread_cleanup_push(cleanup,"1");//这个函数是宏
pthread_cleanup_push(cleanup,"2");
pthread_cleanup_push(cleanup,"3");
//pthread_exit(NULL);
//下面的内容执行不到但是不会报错 会按照全为 1 处理
pthread_cleanup_pop(1);//语法结构一定要对等
pthread_cleanup_pop(0);
pthread_cleanup_pop(1);
puts("push over");
pthread_exit(NULL);
}
int main()
{
pthread_t ptid;
printf("Start !\n");
int err = pthread_create(&ptid,NULL,func,NULL);//ptid 属性 处理函数 传参
if (err){
fprintf(stderr,"pthread_create() : %s\n",strerror(err));
exit(1);
}
pthread_join(ptid,NULL);
printf("End !\n");
exit(0);
}
线程的取消选项
多线程任务 有时需要取消部分任务(线程)
pthread_cancel() 取消有2种状态
- 不允许
- 允许:
1)异步cancel
2)推迟cancel(默认) 推迟到cancel点再响应。
cancel点 : POSIX定义的cancel点 都是可能引发阻塞的系统调用
static void cleanup(void *){
close(*p);
}
//cancel点
fd1 = open();
if (fd1 < 0){
perror();
exit(1);//只要调用exit 不管是 ezit(1) 还是 exit(0) 都是正常终止 都会执行线程清理函数
}
//在这收到一个取消请求 但因为下面的代码没有阻塞的系统调用 所以不会响应
pthread_cleanup_push(cleanup,&fd);
pthread_cleanup_pop(1);
//cancel点 下面的open是阻塞的系统调用 对上面的取消请求做出响应
fd2 = open();
if (fd2 < 0){
perror();
exit(1);
}
pthread_setcancelstate() 设置是否允许取消
pthread_testcancel() 什么都不做 本身就是一个取消点
进程异常终止的条件之一: 最后一个线程对其取消请求作出响应
应用:多线程算质数
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <pthread.h>
#include <unistd.h>
#include <wait.h>
#include <string.h>
#define N 5
#define LEFT 30000000
#define RIGHT 30000200
static void *handler(void *p){
int n = *(int *)p;
int i,j,mark;
for (i = LEFT+n;i <= RIGHT;i+=N){
mark = 1;
for (j = 2;j <= i/2;j++){
if (i%j == 0){
mark = 0;
break;
}
}
if (mark) {
printf("%d is a primer [%d]\n",i,n);
}
}
pthread_exit(p);
}
//交叉算法计算 池类算法涉及到竞争
int main()
{
pthread_t Ptid[N];
void *ptr = NULL;
for (int n = 0;n < N;n++){
int *num = malloc(sizeof(int));
*num = n;
int err = pthread_create(Ptid+n,NULL,handler,num);
if (err){
fprintf(stderr,"%s\n",strerror(err));
exit(1);
}
}
int n;
for (n =0 ;n < N;n++){
pthread_join(Ptid[n],ptr);
free(ptr);
}
exit(0);
}
线程同步 - 互斥量
锁住的是一段代码而不是一个变量
pthread_mutex_t
pthread_mutex_init()
pthread_mutex_destory()
pthread_mutex_lock() 阻塞
pthread_mutex_trylock() 非阻塞
pthread_mutex_unlock()
pthread_once() 动态模块的单词初始化函数
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER 静态初始化,默认属性
互斥量像是bool,非黒即白,没有共享性
互斥量
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static void *handler(void *p){
FILE *fp = fopen(FNAME,"r+");
char buf[BUFSIZE];
if(fp == NULL){
perror("fopen()");
exit(1);
}
//进入临界区
pthread_mutex_lock(&mutex);
fgets(buf,BUFSIZE,fp);
fseek(fp,0,SEEK_SET);
sleep(1);
fprintf(fp,"%d\n",atoi(buf)+1);
fclose(fp);
pthread_mutex_unlock(&mutex);
//离开临界区
pthread_exit(NULL);
}
int main()
{
pthread_t Ptid[THRNUM];
for (int i = 0;i < THRNUM;i++){
int err = pthread_create(Ptid+i,NULL,handler,NULL);
if (err){
fprintf(stderr,"%s\n",strerror(err));
exit(1);
}
}
for (int i = 0;i < THRNUM;i++){
pthread_join(Ptid[i],NULL);
}
pthread_mutex_destroy(&mutex);
return 0;
循环打印 abcd
#define N 4
#define LEFT 30000000
#define RIGHT 30000200
static pthread_mutex_t mutex_arr[N];
static void *handler(void *p){
int n = *(int *)p;
int ch = 'a'+n;
while(1){
pthread_mutex_lock(mutex_arr+n);
write(1,&ch,1);
pthread_mutex_unlock(mutex_arr+(n+1)%N);
}
pthread_exit(p);
}
int main()
{
pthread_t Ptid[N];
void *ptr = NULL;
for (int n = 0;n < N;n++){
int *num = malloc(sizeof(int));
*num = n;
pthread_mutex_init(mutex_arr+n,NULL);
pthread_mutex_lock(mutex_arr+n);
int err = pthread_create(Ptid+n,NULL,handler,num);
if (err){
fprintf(stderr,"%s\n",strerror(err));
exit(1);
}
}
pthread_mutex_unlock(mutex_arr+0);
alarm(3) ;
int n;
for (n =0 ;n < N;n++){
pthread_join(Ptid[n],ptr);
free(ptr);
}
exit(0);
}
线程池
池类算法算质数
思路:多个线程处理num,判断是否是指数,
num > 0 有任务
num = 0 无任务
num = -1 任务结束,main进行收尸
- sched_yield
出让调度器给其他线程,使得其他线程有机会处理num
主线程中发现有任务但是没有线程在处理时是在忙等,可以改为pthread_cond_wait,等下游线程获得任务并修改num==0之后,进行cond broadcast叫醒
注:临界区内所有跳转:解锁再跳转
#define THRNUM 3
#define LEFT 30000000
#define RIGHT 30000200
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;;
static int num = 0;
static void *handler(void *p){
int task,mark;
while(1){
pthread_mutex_lock(&mutex);
while(num == 0){ //num==0,当前无任务,解锁等待
pthread_mutex_unlock(&mutex);//忙等
sched_yield();
pthread_mutex_lock(&mutex);
}
if (num == -1){ //num == -1,任务结束,解锁返回
pthread_mutex_unlock(&mutex);
break;
}
task = num; //成功领取任务
num = 0;
pthread_mutex_unlock(&mutex);
mark = 1;
for (int j = 2;j <= task/2;j++){
if (task%j == 0){
mark = 0;
break;
}
}
if (mark) {
printf("[%d] %d is a priamer\n",(int)p,task);
}
}
pthread_exit(NULL);
}
//池类算法
int main()
{
pthread_t Ptid[THRNUM];
for (int n = 0;n < THRNUM;n++){
int err = pthread_create(Ptid+n,NULL,handler,(void *)n);
if (err){
fprintf(stderr,"%s\n",strerror(err));
exit(1);
}
}
for (int i = LEFT;i <= RIGHT;i++){
pthread_mutex_lock(&mutex);
//任务没有被领取
while(num != 0){
pthread_mutex_unlock(&mutex);
sched_yield();
pthread_mutex_lock(&mutex);
}
//任务已经成功下发
num = i;
pthread_mutex_unlock(&mutex);
}
pthread_mutex_lock(&mutex);
//任务没有被领取
while(num != 0){//确保最后一个任务被其他线程取走
pthread_mutex_unlock(&mutex);
sched_yield();
pthread_mutex_lock(&mutex);
}
//任务已经结束
num = -1;
pthread_mutex_unlock(&mutex);
int n;
for (n =0 ;n < THRNUM;n++){
pthread_join(Ptid[n],NULL);
}
pthread_mutex_destroy(&mutex);
exit(0);
}
线程令牌桶
令牌桶算头文件 与 测试主函数 请见信号篇的 令牌桶
job数组存放1024个令牌桶
-
pthread_once 动态模块的单次初始化
本函数使用初值为PTHREAD_ONCE_INIT的once_control变量保证init_routine()函数在本进程执行序列中仅执行一次。
int pthread_once(pthread_once_t *once_control, void (*init_routine) (void));
pthread_once_t pth_once = PTHREAD_ONCE_INIT; -
nanosleep
int nanosleep(const struct timespec *req, struct timespec rem);
struct timespec {
time_t tv_sec; / seconds /
long tv_nsec; / nanoseconds */
};
暂停某个进程直到规定的时间后恢复
//令牌桶
struct mytbf_st{
int csp;
int burst;
int token;
int pos;//任务列表的下标
pthread_mutex_t mut;
};
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;
//_unlocked表示函数未加锁
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);
//每秒每个令牌桶增加csp个令牌
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_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_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_mutex_unlock(&tbf->mut);
sched_yield();
pthread_mutex_lock(&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_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);
free(tbf);
return 0;
}
线程同步 - 条件变量
pthread_cond_t
pthread_cond_init()
pthread_cond_destory()
pthread_cond_wait() 死等,相当于mutex解锁等待。等通知 + 抢锁
pthread_cond_broadcast() 广播给所有线程,叫醒所有/1个cond wait
pthread_cond_signal() 通知任意一个线程
条件变量可以解决 互斥量进行盲等的问题 即实现了通知法,通知互斥量什么时候上锁
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
static int num = 0;
static void *handler(void *p){
int task,mark;
while(1){
pthread_mutex_lock(&mutex);
while(num == 0){
pthread_cond_wait(&cond,&mutex);
}
if (num == -1){
pthread_mutex_unlock(&mutex);
break;
}
task = num;
num = 0;//成功领取任务
//通知所有线程任务被领取走了
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);
mark = 1;
for (int j = 2;j <= task/2;j++){
if (task%j == 0){
mark = 0;
break;
}
}
if (mark) {
printf("[%d] %d is a priamer\n",*(int *)p,task);
}
}
pthread_exit(NULL);
}
//获取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(&me->cond, &me->mutex); //等待(相当于mutex解锁等待)
}
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;
pthread_cond_broadcast(&me->cond); //叫醒,2个人用一个令牌桶时,一个人还的另一个人可以用
if (tbf->token > tbf->burst)
tbf->token = tbf->burst;
pthread_mutex_unlock(&tbf->mut);
return size;
}
//池类算法
int main()
{
pthread_t Ptid[THRNUM];
for (int n = 0;n < THRNUM;n++){
int *num = malloc(sizeof(int));
*num = n;
int err = pthread_create(Ptid+n,NULL,handler,num);
if (err){
fprintf(stderr,"%s\n",strerror(err));
exit(1);
}
}
for (int i = LEFT;i <= RIGHT;i++){
pthread_mutex_lock(&mutex);
//任务没有被领取
while(num != 0){
pthread_cond_wait(&cond,&mutex);
}
//任务已经成功下发
num = i;
//叫醒任意一个线程执行任务
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
pthread_mutex_lock(&mutex);
//任务没有被领取
while(num != 0){
pthread_cond_wait(&cond,&mutex);
}
//任务已经成功下发
num = -1;
//广播给所有下游线程 任务结束
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);
int n;
for (n =0 ;n < THRNUM;n++){
pthread_join(Ptid[n],NULL);
}
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
exit(0);
}
线程同步 - 信号量
通过互斥量与条件变量的配合我们可以实现信号量
信号量像是一个激活函数 当这个变量超过阈值时 将会触发条件变量给互斥量上锁
信号量应用
用mutex和cond实现有资源上限的资源共享
求RIGHT和LEFT之间的质数,同一时刻只能有4个线程存在
mysem.h
typedef void mysem_t;
mysem_t *mysem_init(int initval);
int mysem_add(mysem_t *, int);
int mysem_sub(mysem_t *, int);
int mysem_destroy(mysem_t *);
mysem.c
#include "mysem.h"
struct mysem_st{
int value; //信号量
pthread_mutex_t mutex;
pthread_cond_t cond;
};
mysem_t *mysem_init(int initval){
struct mysem_st *sem;
sem = malloc(sizeof(*sem));
if (sem == NULL){
return NULL;
}
sem->vaclue = initval;
pthread_mutex_init(&sem->mutex,NULL);
pthread_cond_init(&sem->cond,NULL);
return sem;
}
/* 还资源,叫醒等待进程 */
int mysem_add(mysem_t *ptr,int n){
struct mysem_st *sem = ptr;
pthread_mutex_lock(&sem->mutex);
sem->vaclue += n;
pthread_cond_broadcast(&sem->cond);
pthread_mutex_unlock(&sem->mutex);
return n;
}
/* 取资源 */
int mysem_sub(mysem_t *ptr,int n){
struct mysem_st *sem = ptr;
pthread_mutex_lock(&sem->mutex);
while(sem->vaclue < n){
pthread_cond_wait(&sem->cond,&sem->mutex);
}
sem->vaclue -= n;
pthread_mutex_unlock(&sem->mutex);
return n;
}
int mysem_destory(mysem_t *ptr){
struct mysem_st *sem = ptr;
pthread_mutex_destroy(&sem->mutex);
pthread_cond_destroy(&sem->cond);
free(sem);
return 0;
}
/* 运算线程,线程结束前还资源 */
static void *handler(void *p){
int task,mark= 0;
pthread_mutex_lock(&mutex);
while(num == 0){
pthread_cond_wait(&cond,&mutex);
}
if (num == -1){
pthread_mutex_unlock(&mutex);
mysem_add(sem,1);
pthread_exit(NULL);
}
task = num;
num = 0;//成功领取任务
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);
mark = 1;
for (int j = 2;j <= task/2;j++){
if (task%j == 0){
mark = 0;
//归还计算资源
sleep(1);
mysem_add(sem,1);
pthread_exit(NULL);
}
}
if (mark) {
printf("[%d] %d is a priamer\n",*(int *)p,task);
}
sleep(1);
//归还计算资源
mysem_add(sem,1);
pthread_exit(NULL);
}
main.c
#define THRNUM 20
#define N 4
#define LEFT 30000000
#define RIGHT 30000200
static mysem_t *sem;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;;
static int num = 0;
//池类算法
int main()
{
pthread_t Ptid[THRNUM];
sem = mysem_init(N);//初始化计算资源
for (int i = LEFT;i <= RIGHT;i++){
mysem_sub(sem,1);//消耗一个计算资源
int *ptid = malloc(sizeof(int));
*ptid = i-LEFT;
int err = pthread_create(Ptid+(i-LEFT)%THRNUM,NULL,handler,ptid);
if (err){
fprintf(stderr,"%s\n",strerror(err));
exit(1);
}
pthread_mutex_lock(&mutex);
//任务没有被领取
while(num != 0){
pthread_cond_wait(&cond,&mutex);
}
//任务已经成功下发
num = i;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
pthread_mutex_lock(&mutex);
//任务没有被领取
while(num != 0){
pthread_cond_wait(&cond,&mutex);
}
//任务已经成功下发
num = -1;
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);
int n;
for (n =0 ;n < THRNUM;n++){
pthread_join(Ptid[n],NULL);
}
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
exit(0);
}
线程同步 - 读写锁
读写锁:互斥量和信号量的总和使用
读锁:共享
写锁:互斥
文件加读锁时,写锁等待,其他人看到文件的状态为加了写锁,不能再加读锁,防止写者饿死。
线程属性
pthread_attr_init()
pthread_attr_destory()
pthread_attr_setstacksize() 其他请见 man pthread_attr_init 的 see also
线程同步的属性
互斥量属性
pthread_mutexattr_init()
pthread_mutexattr_destory()
clone 进程 线程 不分家a
跨进程设置锁
pthread_mutexattr_getshared()
pthread_mutexattr_setshared()
pthread_mutexattr_gettype()
pthread_mutexattr_settype()
条件变量属性
pthread_condattr_init()
pthread_condattr_destory()
重入(reentry)
多线程中的IO
getchar_unlocked
线程与信号
pthread_sigmask()
sigwait()
pthread_kill()
openmp
#include <stdio.h>
#include <stdlib.h>
#include <omp.h>
int main()
{
#pragma omp parallel sections
{
#pragma omp section
printf("Hello %d\n",omp_get_thread_num());
#pragma omp section
printf("World %d\n",omp_get_thread_num());
}
exit(0);
}