六.并发之——线程

1.线程的概念
2.线程的创建
  线程的终止
  线程的清理
  线程的取消选项
3.线程同步
4.线程属性
  线程同步的属性
5. 重入
  线程与信号
  线程与fork

1.概念

一个正在运行的函数
posix线程是一套标准,而不是实现
openmp标准线程
线程标识:pthread_t
函数:
(1).pthread_equal 比较两个线程号
在这里插入图片描述

(2).pthread_self :获取线程号

在这里插入图片描述

2.线程的创建

函数:pthread_create
在这里插入图片描述
参数:1.回填标识;2.只读标识;3.函数入口地址;4:函数参数

返回值:
On success, pthread_create() returns 0; on error, it returns an error number, and the contents of *thread are undefined.

线程的调度取决于调度器的策略

收尸函数:
在这里插入图片描述
第二个参数为NULL表示只收尸不关心状态。
RETURN VALUE
On success, pthread_join() returns 0; on error, it returns an error number.

#include<stdio.h>
#include<stdlib.h>
#include <string.h>
#include<pthread.h>
static void* func(void*) {
	puts("Thread is working!");
	pthread_exit(NULL);
	//return NULL;
}
int main() {
	pthread_t tid;
	int err;
	puts("Begin!");

	err = pthread_create(&tid, NULL, func, NULL);
	if (err) {
		fprintf(stderr, "pthread_create(): %s\n", strerror(err));
		exit(1);
	}
	int erro = pthread_join(tid, NULL);
	if (erro) {
		fprintf(stderr, "pthread_join(): %s\n", strerror(erro));
		exit(1);
	}
	puts("End!");
	
	exit(0);
}
//Begin!
//Thread is working!
//End!

3.线程的终止

3.1终止的3种方式:

1)线程从启动历程返回,返回值就是线程的退出码;
2) 线程可以被同一进程中的其他线程取消
3)线程调用pthread_exit()函数
在这里插入图片描述
在这里插入图片描述
pthread_cleanup_pop可以写在pthread_exit之后(执行到pthread_exit,还没有pop的默认执行pthread_cleanup_pop(1);//1表示调用,0表示不调用)

3.2栈的清理

函数:

在这里插入图片描述

#include<stdio.h>
#include<stdlib.h>
#include <string.h>
#include<pthread.h>
static void cleanup_fuc(void* p) {
	puts((const char*)p);
}
static void* func(void* p) {
	puts("Thread is working!");
	pthread_cleanup_push(cleanup_fuc, (void*)"cleanup:1");
	pthread_cleanup_push(cleanup_fuc, (void*)"cleanup:2");
	pthread_cleanup_push(cleanup_fuc, (void*)"cleanup:3");
	//push和pop必须成对出现(因为这两个函数是宏定义的,一对大括号分别落在这两个函数中)
	pthread_cleanup_pop(1);//1表示调用,0表示不调用
	pthread_cleanup_pop(0);
	pthread_cleanup_pop(1);
	pthread_exit(NULL);
	//return NULL;
}
int main() {
	pthread_t tid;
	int err;
	puts("Begin!");

	err = pthread_create(&tid, NULL, func, NULL);
	if (err) {
		fprintf(stderr, "pthread_create(): %s\n", strerror(err));
		exit(1);
	}

	pthread_join(tid, NULL);
	
	puts("End!");
	
	exit(0);
}
/*

	Begin!
	Thread is working!
	cleanup:3
	cleanup:1
	End!


*/

4.线程的取消

函数:
在这里插入图片描述
RETURN VALUE
On success, pthread_cancel() returns 0; on error, it returns a nonzero error number.

线程的取消选项:允许和不允许
允许取消又分为:异步cancel和推迟cancel(默认)–>推迟到cancel点
cancel点:POSIX定义的cancel点,都是可能引发阻塞的系统调用

函数:
pthread_setcancelstate:设置是否允许取消
pthread_setcanceltype:设置取消方式
pthread_testcancel:本函数什么都不做,就是一个取消点

线程分离:
int pthread_detach(pthread_t thread);

5.线程——竞争实例

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>

#define LEFT 30000000
#define RIGHT 30000200
#define THRNUM (RIGHT - LEFT + 1)


static void* thr_prime(void *i) {
    int num = *((int*)i) ;
  
    int mark = 1;
    for (int j = 2; j < num / 2; j++) {
        if (num % j == 0) {
            mark = 0;
            break;
        }
    }
    if (mark)
        printf("%d is a primer\n", num);
	pthread_exit(NULL); 
}

int main(int argc, char** argv) {
	pthread_t tid[THRNUM];//线程数组
	int mark, err;
 
    for (int i = LEFT; i <= RIGHT; i++) {
          
		err = pthread_create(tid + i - LEFT , NULL, thr_prime, &i);//出问题的语句!!!!
        
        if(err) {
            fprintf(stderr, "pthread_create(): %s\n", strerror(err));
            exit(1);
        }
            

	}

	
	for (int i = LEFT; i <= RIGHT; i++) {
		pthread_join(tid[i - LEFT], NULL);//接收thr_prime返回的指针
        
	}
    
	exit(0);

}


结果:每次都不一样,正确应该是18个(发生竞争)
在这里插入图片描述

原因是:所有线程入口函数参数在共享地址i,前面的线程可能还没把i拿走,就创建了第二个线程。

改进:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>

#define LEFT 30000000
#define RIGHT 30000200
#define THRNUM (RIGHT - LEFT + 1)

struct thr_arg_st {
    int n;
};

static void* thr_prime(void *i) {
    int num = ((struct thr_arg_st*)i) -> n;
  
    int mark = 1;
    for (int j = 2; j < num / 2; j++) {
        if (num % j == 0) {
            mark = 0;
            break;
        }
    }
    if (mark)
        printf("%d is a primer\n", num);
	pthread_exit(i); //记录返回状态为地址i
}

int main(int argc, char** argv) {
	pthread_t tid[THRNUM];//线程数组
	int mark, err;
    struct thr_arg_st * p = NULL;
    void * ptr = NULL;
    for (int i = LEFT; i <= RIGHT; i++) {
        p = malloc(sizeof(*p));     //malloc
        if(p == NULL) {
            perror("malloc()");
            exit(1);
        }
        p->n = i;
		err = pthread_create(tid + i - LEFT, NULL, thr_prime, p);//p的地址不同了
        
        if(err) {
            fprintf(stderr, "pthread_create(): %s\n", strerror(err));
            exit(1);
        }
            

	}

	for (int i = LEFT; i <= RIGHT; i++) {
		pthread_join(tid[i - LEFT], &ptr);//接收thr_prime返回的指针
        free(ptr);      //free 
	}
	exit(0);

}

/*

	30000059 is a primer
	30000023 is a primer
	30000083 is a primer
	30000163 is a primer
	30000169 is a primer
	30000079 is a primer
	30000037 is a primer
	30000109 is a primer
	30000071 is a primer
	30000041 is a primer
	30000133 is a primer
	30000049 is a primer
	30000193 is a primer
	30000199 is a primer
	30000149 is a primer
	30000137 is a primer
	30000167 is a primer
	30000001 is a primer


*/

6.竞争故障

有问题的代码:

初始值1,每个线程执行加1,正确应该是21

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>

#define         THRNUM         20
#define         LINESIZE       1024
#define         FNAME           "/tmp/out"
//static pthread_mutex_t mut[THRNUM] ;
static void* thr_add(void *p) {
   FILE* fp = fopen(FNAME,"r+");
   char linebuf[LINESIZE];
   if(fp == NULL) {
       perror("fopen()");
       exit(1);
   }
   
   fgets(linebuf, LINESIZE, fp);
   fseek(fp, 0, SEEK_SET);      //定位文件第一行数字
   sleep(1);			//所有线程停在这里, 读到的数字都是1
   fprintf(fp, "%d\n", atoi(linebuf) + 1);
   fclose(fp);
   pthread_exit(NULL);
}

int main(int argc, char** argv) {
	pthread_t tid[THRNUM];//线程数组
	int mark, err;
 
    for (int i = 0; i < THRNUM; i++) {
       
		err = pthread_create(tid + i  , NULL, thr_add, NULL);
        
        if(err) {
            fprintf(stderr, "pthread_create(): %s\n", strerror(err));
            exit(1);
        }
            

	}
    
	for (int i = 0; i < THRNUM; i++) {
		pthread_join(tid[i], NULL);//收尸    
	}

	exit(0);

}


输出的是2
在这里插入图片描述

7.互斥量

函数:

pthread_mutex_init动态初始化
pthread_mutex_destroy
在这里插入图片描述
lock会一直等待,trylock不等
在这里插入图片描述
改进代码:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>

#define         THRNUM         20
#define         LINESIZE       1024
#define         FNAME           "/tmp/out"
static pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER ;
static void* thr_add(void *p) {
   FILE* fp = fopen(FNAME,"r+");
   char linebuf[LINESIZE];
   if(fp == NULL) {
       perror("fopen()");
       exit(1);
   }
   pthread_mutex_lock(&mut); //lock
   fgets(linebuf, LINESIZE, fp);
   fseek(fp, 0, SEEK_SET);      //定位文件第一行数字
   sleep(1);				//停1s
   fprintf(fp, "%d\n", atoi(linebuf) + 1);
   fclose(fp);
   pthread_mutex_unlock(&mut); //unlock
   pthread_exit(NULL);
}

int main(int argc, char** argv) {
	pthread_t tid[THRNUM];//线程数组
	int mark, err;
 
    for (int i = 0; i < THRNUM; i++) {
       
		err = pthread_create(tid + i  , NULL, thr_add, NULL);
        
        if(err) {
            fprintf(stderr, "pthread_create(): %s\n", strerror(err));
            exit(1);
        }
            

	}
    
	for (int i = 0; i < THRNUM; i++) {
		pthread_join(tid[i], NULL);//收尸    
	}
    pthread_mutexattr_destroy(&mut);//销毁互斥量
	exit(0);

}


在这里插入图片描述

实例2:

依次打印abcd循环5s

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>

#define         THRNUM         4


static pthread_mutex_t mut[THRNUM] ;
static void* thr_func(void *p) {
    int n = (int)p;
    char c = 'a' + n;
    while(1) {
        pthread_mutex_lock(mut + n);	//锁住自己
        write(1, &c, 1);
        pthread_mutex_unlock(mut + (n + 1) % THRNUM);	//松开下一个
    }
        
	pthread_exit(NULL); 
}

int main(int argc, char** argv) {
	pthread_t tid[THRNUM];//线程数组
	int mark, err;
 
    for (int i = 0; i < THRNUM; i++) {
        pthread_mutex_init(mut + i, NULL);
        pthread_mutex_lock(mut + i);
		err = pthread_create(tid + i  , NULL, thr_func, (void*)i);
        
        if(err) {
            fprintf(stderr, "pthread_create(): %s\n", strerror(err));
            exit(1);
        }
            

	}
    pthread_mutex_unlock(mut + 0);//解开第一个
    alarm(5);  //5s后发出信号SIGALRM中断进程
	
	for (int i = 0; i < THRNUM; i++) {
		pthread_join(tid[i], NULL);//收尸    
	}
    //pthread_mutexattr_destroy(&mut);//销毁互斥量
	exit(0);

}

//输出:循环打印abcd, 5秒后终止

每次锁住自己,等上一个解开自己

8.池类写法(4个线程处理200个数)

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>

#define     LEFT        30000000
#define     RIGHT       30000200
#define     THRNUM      4

static int num = 0;
static pthread_mutex_t mut_num = PTHREAD_MUTEX_INITIALIZER;
//计算一个区间内的质数, 放入一个数num,4个线程抢着计算,取走数了num变为0,等计算完了再放下一个数,全部算完了num=-1
//num>0是任务放入一个数 0是这个数取走了等待放入下一个数  -1退出

static void* thr_prime(void *p) {
    int i, j, mark;
    while(1) {
        pthread_mutex_lock(&mut_num);
        while(num == 0) {       //没有新任务(之前的数字取走了,新的数还没放进来)
            pthread_mutex_unlock(&mut_num); //松开 ,让别人尽快放进来数
            sched_yield();
            pthread_mutex_lock(&mut_num);//加锁
        }

        if(num == -1) {     //可能已经结束
            pthread_mutex_unlock(&mut_num);//不能忘记unlock
            break;
        }
        i = num;
        num = 0;
        pthread_mutex_unlock(&mut_num); 

        mark = 1;

        for (int j = 2; j < i / 2; j++) {
            if (i % j == 0) {
                mark = 0;
                break;
            }
        }
        if (mark)
            printf("[%d]%d is a primer\n", (int)p, i);//打印出是几号线程处理的
    }
    
	pthread_exit(NULL); 
}

int main(int argc, char** argv) {
	pthread_t tid[THRNUM];//线程数组
	int  err;

    for (int i = 0; i < THRNUM; i++) {
		err = pthread_create(tid + i , NULL, thr_prime, (void*)i);
        
        if(err) {
            fprintf(stderr, "pthread_create(): %s\n", strerror(err));
            exit(1);
        }
	}

    for(int i = LEFT; i <= RIGHT; i++) {
        pthread_mutex_lock(&mut_num);
        while(num != 0) {       //num是0才赋值
            pthread_mutex_unlock(&mut_num); //松开,让别人有机会加锁(可能之前的数还没被取走,让别人尽快取走!)
            sched_yield();//出让调度器给别的线程,可以理解为很短的sleep, 且不会造成当前进程的调度颠簸
            pthread_mutex_lock(&mut_num);
        }
        num = i;            //继续放入数
        pthread_mutex_unlock(&mut_num);
    }
    pthread_mutex_lock(&mut_num);
    while(num != 0) {           //防止最后的数还没处理num就被写成-1
        pthread_mutex_unlock(&mut_num); //松开
        sched_yield();
        pthread_mutex_lock(&mut_num);//加锁
    }
    num = -1; //退出
    pthread_mutex_unlock(&mut_num);
	for (int i = 0; i <= THRNUM; i++) {
		pthread_join(tid[i], NULL);//接收thr_prime返回的指针
     
	}
    pthread_mutex_destroy(&mut_num);
	exit(0);

}





结果:
能者多劳,哪个线程抢到就哪个线程计算

在这里插入图片描述

改进之处:可以用通知法减少盲等

9.线程令牌桶

函数:pthread_once,保证一个函数只被调用一次
在这里插入图片描述

mtbf.h

#ifndef MYTBF_H__
#define MYTBF_H__

#define MYTBF_MAX 1024

typedef void mytbf_t;

mytbf_t* mytbf_init(int cps, int burst);
int mytbf_fetchtoken(mytbf_t *, int);		//取走多少令牌
int mytbf_returntoken(mytbf_t*, int);		//归还了多少
int mytbf_destroy(mytbf_t *);


#endif

mytbf.c

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<pthread.h>
#include <unistd.h>
#include"mytbf.h"
#include<string.h>


static struct mytbf_st* job[MYTBF_MAX];	//结构体指针数组 自动全0
static pthread_mutex_t mut_job = PTHREAD_MUTEX_INITIALIZER;		//这个互斥量保证job数组以独占形式存在
static int inited = 0;
static pthread_t tid_alrm;
static pthread_once_t init_once = PTHREAD_ONCE_INIT;
struct mytbf_st {
	int cps;	//速率
	int burst;	//上限
	int token;
	int pos;	//在job数组的位置
	pthread_mutex_t mut;	//这个互斥量保证token以独占形式存在(mytbf_fetchtoken和mytbf_returntoken一个对token减一个对token加容易发生竞争)
};

static void * thr_alrm(void* p) {
	while (1) {
		pthread_mutex_lock(&mut_job);		//锁job数组
		for (int i = 0; i < MYTBF_MAX; i++) {
			if (job[i] != NULL) {
				pthread_mutex_lock(&job[i]->mut);//锁token
				job[i]->token += job[i]->cps;		//每一秒加上每秒传输字节数CPS到token(每个token表示一字节)
				if (job[i]->token > job[i]->burst)
					job[i]->token = job[i]->burst;//不超过上限
				pthread_mutex_unlock(&job[i]->mut);
			}
		}
		pthread_mutex_unlock(&mut_job);
		sleep(1);//1s做一次
	}
	
}
static void module_unload();
static void module_load() {
	
	int err;
	err =  pthread_create(&tid_alrm, NULL, thr_alrm, NULL);
	if (err) {
		fprintf(stderr, "pthreda_create():%s\n", strerror(err));
		exit(1);
	}
	atexit(module_unload);		//复原
}
static void module_unload() {
	
	pthread_cancel(tid_alrm);
	pthread_join(tid_alrm, NULL);
	for (int i = 0; i < MYTBF_MAX; i++) {
		if (job[i] != NULL) {
			mytbf_destroy(job[i]);
		}
		//free(job[i]);
	}
	pthread_mutex_destroy(&mut_job);
}
static int get_free_pos_unlocked() {
	for (int i = 0; i < MYTBF_MAX; i++) {
		if (job[i] == NULL)	
			return i;
		return -1;			//没空地了
	}
}
static int min(int a, int b) {
	return a < b ? a : b;
}
mytbf_t* mytbf_init(int cps, int burst) {
	struct mytbf_st * me;

	//if (!inited) {//保证只调用一次
	//	module_load();
	//	inited = 1;
	//}
	pthread_once(&init_once, module_load);//保证只调用一次
	me = malloc(sizeof(*me));
	if (me == NULL)
		return NULL;

	me->token = 0;
	me->cps = cps;
	me->burst = burst;
	pthread_mutex_init(&me->mut, NULL);
	//----------临界区--------------------
	pthread_mutex_lock(&mut_job);
	int pos = get_free_pos_unlocked();
	if (pos < 0) {	//没空地了
		pthread_mutex_unlock(&mut_job);
		free(me);
		return NULL;
	}
	me->pos = pos;
	job[pos] = me;
	pthread_mutex_unlock(&mut_job);
	//-----------------------------------
	
	
	
	
	return me;
}
int mytbf_fetchtoken(mytbf_t * ptr, int size) {
	struct mytbf_st * me = ptr;//void* 转换成 struct mytbf_st *
	if (size <= 0)
		return -EINVAL;	//参数非法
	pthread_mutex_lock(&me->mut);
	while (me->token <= 0) {	//等到有token为止
		pthread_mutex_unlock(&me->mut);
		sched_yield();				//出让调度器
		pthread_mutex_lock(&me->mut);
	}
	int n = min(me->token, size);
	me->token -= n;
	pthread_mutex_unlock(&me->mut);
	return n;

}	
int mytbf_returntoken(mytbf_t* ptr, int size) {
	struct mytbf_st * me = ptr;//void* 转换成 struct mytbf_st *
	if (size <= 0)
		return -EINVAL;	//参数非法

	pthread_mutex_lock(&me->mut);
	me->token += size;
	if (me->token > me->burst)	//大于上限
		me->token = me->burst;
	pthread_mutex_unlock(&me->mut);

	return size;
}
int mytbf_destroy(mytbf_t *ptr) {
	struct mytbf_st * me = ptr;
	pthread_mutex_lock(&mut_job);
	job[me->pos] = NULL;
	pthread_mutex_unlock(&mut_job);
	pthread_mutex_destroy(&me->mut);
	free(ptr);
	return 0;
}

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<signal.h>
#include"mytbf.h"
#include <string.h>
#define CPS     10         //每秒10字节
#define BUFSIZE 1024
#define BURST   100			//上限,限制token值	



int main(int argc, char** argv) {
	int sfd, dfd = 1;			//输出到标准输出
	char buf[BUFSIZE];
	int len, ret;
	int pos = 0;
	int size;
	mytbf_t * tbf;
	if (argc != 2) {
		fprintf(stderr, "Usage:....\n");
		exit(1);
	}

	//signal(SIGALRM, alrm_handler);
	//alarm(1);

	do {
		sfd = open(argv[1], O_RDONLY);//不管真错,还是假错(信号中断),都返回-1
		if (sfd < 0) {
			if (errno != EINTR) {//不是信号打断系统调用,是真的错,直接退出,如果是假错,则继续open
				perror("open()");
				exit(1);
			}

		}
	} while (sfd < 0);

	tbf = mytbf_init(CPS, BURST);
	if (tbf == NULL) {
		fprintf(stderr, "mytbf_init() failed!\n");
		exit(1);
	}

	while (1) {
		size = mytbf_fetchtoken(tbf, BUFSIZE);
		if (size < 0) {
			fprintf(stderr, "mytbf_fetchtoken():%s\n", strerror(-size));
			exit(1);
		}
		while ((len = read(sfd, buf, size)) < 0) {	
			
			if (errno == EINTR)//假错中断
				continue;
			
			perror("read()");
			break;
		}
		if (len == 0) {//读完了
			break;
		}
		if (size - len > 0)		//需要归还
			mytbf_returntoken(tbf, size - len);
		pos = 0;
		while (len > 0) {//防止真正写进去的数少于读进来的数
			ret = write(dfd, buf + pos, len);//写入失败返回-1
			if (ret < 0) {
				///
				if (errno == EINTR)//假错中断
					continue;
				///
				perror("write()");
				exit(1);
			}
			pos += ret;
			len -= ret;
		}

	}
	//close(dfd);
	close(sfd);
	mytbf_destroy(tbf);
	exit(0);
}




10.条件变量

函数:

pthread_cond_init动态初始化
pthread_cond_destroy
在这里插入图片描述

pthread_cond_broadcast全通知
pthread_cond_signal只唤醒一个
在这里插入图片描述
pthread_cond_wait死等
pthread_cond_timewait
在这里插入图片描述

例子1

上面的线程令牌桶mytbf.c加上条件变量(非盲等版本):

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<pthread.h>
#include <unistd.h>
#include"mytbf.h"
#include<string.h>


static struct mytbf_st* job[MYTBF_MAX];	//结构体指针数组 自动全0
static pthread_mutex_t mut_job = PTHREAD_MUTEX_INITIALIZER;		//这个互斥量保证job数组以独占形式存在
static int inited = 0;
static pthread_t tid_alrm;
static pthread_once_t init_once = PTHREAD_ONCE_INIT;
struct mytbf_st {
	int cps;	//速率
	int burst;	//上限
	int token;
	int pos;	//在job数组的位置
	pthread_mutex_t mut;	//这个互斥量保证token以独占形式存在(mytbf_fetchtoken和mytbf_returntoken一个对token减一个对token加容易发生竞争)
	pthread_cond_t cond;	//条件变量!!!!!
};

static void * thr_alrm(void* p) {
	while (1) {
		pthread_mutex_lock(&mut_job);		//锁job数组
		for (int i = 0; i < MYTBF_MAX; i++) {
			if (job[i] != NULL) {
				pthread_mutex_lock(&job[i]->mut);//锁token
				job[i]->token += job[i]->cps;		//每一秒加上每秒传输字节数CPS到token(每个token表示一字节)
				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(&mut_job);
		sleep(1);//1s做一次
	}
	
}
static void module_unload();
static void module_load() {
	
	int err;
	err =  pthread_create(&tid_alrm, NULL, thr_alrm, NULL);
	if (err) {
		fprintf(stderr, "pthreda_create():%s\n", strerror(err));
		exit(1);
	}
	atexit(module_unload);		//复原
}
static void module_unload() {
	
	pthread_cancel(tid_alrm);
	pthread_join(tid_alrm, NULL);
	for (int i = 0; i < MYTBF_MAX; i++) {
		if (job[i] != NULL) {
			mytbf_destroy(job[i]);
		}
		//free(job[i]);
	}
	pthread_mutex_destroy(&mut_job);
}
static int get_free_pos_unlocked() {
	for (int i = 0; i < MYTBF_MAX; i++) {
		if (job[i] == NULL)	
			return i;
		return -1;			//没空地了
	}
}
static int min(int a, int b) {
	return a < b ? a : b;
}
mytbf_t* mytbf_init(int cps, int burst) {
	struct mytbf_st * me;

	//if (!inited) {//保证只调用一次
	//	module_load();
	//	inited = 1;
	//}
	pthread_once(&init_once, module_load);//保证只调用一次
	me = malloc(sizeof(*me));
	if (me == NULL)
		return NULL;

	me->token = 0;
	me->cps = cps;
	me->burst = burst;
	pthread_mutex_init(&me->mut, NULL);
	pthread_cond_init(&me->cond, NULL);
	//----------临界区--------------------
	pthread_mutex_lock(&mut_job);
	int pos = get_free_pos_unlocked();
	if (pos < 0) {	//没空地了
		pthread_mutex_unlock(&mut_job);
		free(me);
		return NULL;
	}
	me->pos = pos;
	job[pos] = me;
	pthread_mutex_unlock(&mut_job);
	//-----------------------------------
	
	return me;
}
int mytbf_fetchtoken(mytbf_t * ptr, int size) {
	struct mytbf_st * me = ptr;//void* 转换成 struct mytbf_st *
	if (size <= 0)
		return -EINVAL;	//参数非法
	pthread_mutex_lock(&me->mut);
	while (me->token <= 0) {	//等到有token为止
	//	pthread_mutex_unlock(&me->mut);
	//	sched_yield();				//出让调度器
	//	pthread_mutex_lock(&me->mut);

		pthread_cond_wait(&me->cond, &me->mut);//换成这一句,不再盲等(得到通知后先去抢锁,再看是否可以退出while,成立则继续运行)!!!
	}
	
	int n = min(me->token, size);
	me->token -= n;
	pthread_mutex_unlock(&me->mut);
	return n;

}	
int mytbf_returntoken(mytbf_t* ptr, int size) {
	struct mytbf_st * me = ptr;//void* 转换成 struct mytbf_st *
	if (size <= 0)
		return -EINVAL;	//参数非法

	pthread_mutex_lock(&me->mut);
	me->token += size;
	if (me->token > me->burst)	//大于上限
		me->token = me->burst;
	pthread_cond_broadcast(&me->cond);//还了cond后也通知一下 !!!
	pthread_mutex_unlock(&me->mut);

	return size;
}
int mytbf_destroy(mytbf_t *ptr) {
	struct mytbf_st * me = ptr;
	pthread_mutex_lock(&mut_job);
	job[me->pos] = NULL;
	pthread_mutex_unlock(&mut_job);
	pthread_mutex_destroy(&me->mut);
	pthread_cond_destroy(&me->cond);
	free(ptr);
	return 0;
}

例子2

池类写法,4个线程处理200个数的非盲等版本

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>

#define     LEFT        30000000
#define     RIGHT       30000200
#define     THRNUM      4

static int num = 0;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;//!!!!!!!!!!!!!!!!!!!!!!!!!!!
static pthread_mutex_t mut_num = PTHREAD_MUTEX_INITIALIZER;
//计算一个区间内的质数, 放入一个数num,4个线程抢着计算,取走数了num变为0,等计算完了再放下一个数,全部算完了num=-1
//num>0是任务放入一个数 0是这个数取走了等待放入下一个数  -1退出



//下游函数
static void* thr_prime(void *p) {
	int i, j, mark;
	while (1) {
		pthread_mutex_lock(&mut_num);
		while (num == 0) {       //没有新任务(之前的数字取走了,新的数还没放进来)
			//pthread_mutex_unlock(&mut_num); //松开 ,让别人尽快放进来数
			//sched_yield();
			//pthread_mutex_lock(&mut_num);//加锁
			pthread_cond_wait(&cond, &mut_num);
		}

		if (num == -1) {     //可能已经结束
			pthread_mutex_unlock(&mut_num);//不能忘记unlock
			break;
		}
		i = num;
		num = 0;
		pthread_cond_broadcast(&cond);//!!!!!!!!!!!!!!!!!!!!!!!!!唤醒所有(以免只唤醒一个和自己一样在这个下游函数里的线程)
		pthread_mutex_unlock(&mut_num);

		mark = 1;

		for (int j = 2; j < i / 2; j++) {
			if (i % j == 0) {
				mark = 0;
				break;
			}
		}
		if (mark)
			printf("[%d]%d is a primer\n", (int)p, i);//打印出是几号线程处理的
	}

	pthread_exit(NULL);
}

int main(int argc, char** argv) {
	pthread_t tid[THRNUM];//线程数组
	int  err;

	for (int i = 0; i < THRNUM; i++) {
		err = pthread_create(tid + i, NULL, thr_prime, (void*)i);

		if (err) {
			fprintf(stderr, "pthread_create(): %s\n", strerror(err));
			exit(1);
		}
	}

	for (int i = LEFT; i <= RIGHT; i++) {
		pthread_mutex_lock(&mut_num);
		while (num != 0) {       //num是0才赋值(可能还没被取走,还不能放入下一个数)
			//pthread_mutex_unlock(&mut_num); //松开,让别人有机会加锁(可能之前的数还没被取走,让别人尽快取走!)
			//sched_yield();//出让调度器给别的线程,可以理解为很短的sleep, 且不会造成当前进程的调度颠簸
			//pthread_mutex_lock(&mut_num);
			pthread_cond_wait(&cond, &mut_num);  //!!!!!!!!!!!!!!!!!!!!!!!!!!
		}
		num = i;            //继续放入数
		pthread_cond_signal(&cond);//!!!!!!!!!!!!!!!!!!!!!!唤醒一个就行(唤醒下游函数任意一个就行)
		pthread_mutex_unlock(&mut_num);
	}
	pthread_mutex_lock(&mut_num);
	while (num != 0) {           //防止最后的数还没处理num就被写成-1
		//pthread_mutex_unlock(&mut_num); //松开
		//sched_yield();
		//pthread_mutex_lock(&mut_num);//加锁
		pthread_cond_wait(&cond, &mut_num);
	}
	num = -1; //退出
	pthread_cond_broadcast(&cond);//!!!!!!!!!!!!!!!!
	pthread_mutex_unlock(&mut_num);
	for (int i = 0; i <= THRNUM; i++) {
		pthread_join(tid[i], NULL);//接收thr_prime返回的指针

	}

	pthread_mutex_destroy(&mut_num);
	pthread_cond_destroy(&cond);
	exit(0);

}

例子3

5s循环打印abcd

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
#include <unistd.h>

#define         THRNUM         4

static int num = 0;
static pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
static void* thr_func(void *p) {
	int n = (int)p;		//线程号
	char c = 'a' + n;
	while (1) {
		pthread_mutex_lock(&mut);	
		while (num != n) {	//看看有没有轮到打印自己	
			pthread_cond_wait(&cond, &mut);//解锁等待
		}
		write(1, &c, 1);
		num = (n + 1) % THRNUM;	//变为下一个数
		pthread_cond_broadcast(&cond);//唤醒所有(不用signal是不确定是不是下个线程会抢到)
		pthread_mutex_unlock(&mut);	
	}

	pthread_exit(NULL);
}

int main(int argc, char** argv) {
	pthread_t tid[THRNUM];//线程数组
	int mark, err;

	for (int i = 0; i < THRNUM; i++) {
		err = pthread_create(tid + i, NULL, thr_func, (void*)i);

		if (err) {
			fprintf(stderr, "pthread_create(): %s\n", strerror(err));
			exit(1);
		}


	}
	
	alarm(5);  //5s后发出信号SIGALRM中断进程

	for (int i = 0; i < THRNUM; i++) {
		pthread_join(tid[i], NULL);//收尸    
	}
	pthread_mutex_destroy(&mut);
	pthread_cond_destroy(&cond);
	exit(0);

}

11.信号量

互斥量+条件变量实现信号量机制(资源数为4)
mysem.h

#ifndef MYSEM_H__
#define MYSEM_H__


typedef void mysem_t;

mysem_t* mysem_init(int initval);			//资源总量
int mysem_add(mysem_t *, int);			//取走资源量 p操作
int mysem_sub(mysem_t*, int);			//归还资源量 v操作
int mysem_destroy(mysem_t *);


#endif

mysem.c

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<pthread.h>
#include <unistd.h>
#include"mysem.h"
#include<string.h>

static pthread_mutex_t mut_job = PTHREAD_MUTEX_INITIALIZER;		//这个互斥量保证job数组以独占形式存在


struct mysem_st {
	int value;
	pthread_mutex_t mut;	//互斥量
	pthread_cond_t cond;	//条件变量
};



mysem_t* mysem_init(int initval) {
	struct mysem_st * me;
	me = malloc(sizeof(*me));
	if (me == NULL)
		return NULL;
	me->value = initval;
	pthread_mutex_init(&me->mut, NULL);
	pthread_cond_init(&me->cond, NULL);
	return me;
}
int mysem_sub(mysem_t *ptr, int n) {	//取走资源
	struct mysem_st * me = ptr;
	pthread_mutex_lock(&me->mut);

	while (me->value < n)			//资源不够
		pthread_cond_wait(&me->cond, &me->mut);
	me->value -= n;
	pthread_mutex_unlock(&me->mut);
	return n;
}
int mysem_add(mysem_t* ptr, int n ) {//还资源
	struct mysem_st * me = ptr;
	pthread_mutex_lock(&me->mut);
	
	me->value += n;
	pthread_cond_broadcast(&me->cond);//有资源了,通知一下
	pthread_mutex_unlock(&me->mut);
	return n;
}
int mysem_destroy(mysem_t * ptr) {
	struct mysem_st * me = ptr;
	pthread_mutex_destroy(&me->mut);
	pthread_cond_destroy(&me->cond);
	free(me);
	return 0;
}

main.c

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include"mysem.h"
#include<pthread.h>

#define			LEFT		30000000
#define			RIGHT		30000200
#define			THRNUM		(RIGHT - LEFT + 1)

#define			N			4						//同一时刻最多4个线程跑


static mysem_t* sem;
static void* thr_prime(void *i) {
	int num = (int)i;

	int mark = 1;
	for (int j = 2; j < num / 2; j++) {
		if (num % j == 0) {
			mark = 0;
			break;
		}
	}
	if (mark)
		printf("%d is a primer\n", num);
	mysem_add(sem, 1);	//归还资源量 v操作
	pthread_exit(NULL);
}

int main(int argc, char** argv) {
	pthread_t tid[THRNUM];//线程数组
	int mark, err;
	
	sem = mysem_init(N);
	if (sem == NULL) {
		fprintf(stderr, "mysem_init() failed!\n");
		exit(1);
	}
	for (int i = LEFT; i <= RIGHT; i++) {		//201个线程每个处理一个数字
		mysem_sub(sem, 1);取走资源量 p操作
		err = pthread_create(tid + i - LEFT, NULL, thr_prime, (void*)i);

		if (err) {
			fprintf(stderr, "pthread_create(): %s\n", strerror(err));
			exit(1);
		}
	}


	for (int i = LEFT; i <= RIGHT; i++) {
		pthread_join(tid[i - LEFT], NULL);//接收thr_prime返回的指针

	}
	mysem_destroy(sem);
	exit(0);

}

12.线程属性

读写锁:读锁相当于共享锁,写锁相当于互斥锁
函数:
在这里插入图片描述
RETURN VALUE
On success, these functions return 0; on error, they return a nonzero error number.

pthread_attr_setstacksize:function sets the stack size attribute of the thread attributes object referred to by attr to the value specified in stacksize.
更多属性见man pthread_attr_init的see also

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<string.h>
#include<pthread.h>

static  void* func(void* p) {
	while (1)
		pause();
	pthread_exit(NULL);

}

int main(int argc, char** argv) {                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
	pthread_t tid;
	pthread_attr_t attr;
	pthread_attr_init(&attr);
	pthread_attr_setstacksize(&attr, 1024 * 1024);//1M大小
	int i;
	for (i = 0;; i++) {
		int err = pthread_create(&tid, &attr, func, NULL);
		if (err) {
			fprintf(stderr, "pthraed_create():%s\n", strerror(err));
			break;
		}
	}
	printf("%d\n", i);
	pthread_attr_destroy(&attr);
	exit(0);

}
//输出5076

13.互斥量属性

函数:
pthread_mutexattr_init(pthread_mutexattr_t *attr)
pthread_mutexattr_destroy
pthread_mutexattr_getpshared
pthread_mutexattr_setpshared
clone
pthread_mutexattr_gettype
pthread_mutexattr_settype

14.条件变量属性

pthread_condattr_init
pthread_condattr_destroy

15.重入

很多标准io都可以实现多线程并发
3个线程分别打印‘aaa’,‘bbb’,‘ccc’,结果不会出现abcababcc这样的情况(内部已经实现互斥了)
不能实现线程安全的会在名字上体现在这里插入图片描述

12.管道实例——池类写法

扩大池的容量

mypipe.h

#ifndef MYPIPE_H__
#define MYPIPE_H__

//#include <stdint.h>

#define		PIPESIZE		 1024      //管道大小
#define		MYPIPE_READ      0x00000001UL		//位图,1是读者
#define		MYPIPE_WRITE     0x00000010UL		//2是写者
typedef void mypipe_t;

mypipe_t* mypipi_init();
int mypipe_read(mypipe_t*, void* buf, size_t size); //真正读了多少数据
int mypipe_write(mypipe_t*, const void*, size_t);	//真正写了多少数据
int mypipe_destroy(mypipe_t*);

int mypipe_register(mypipe_t*, int);
int mypipe_unregister(mypipe_t*, int);

#endif

mypipe.c

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
#include"mypipe.h"



struct mypipe_st {
	int head;    //队头
	int tail;	 //队尾
	int datasize;//记录当前管道有多少有效字节数
	char data[PIPESIZE];
	int count_rd;
	int count_wr;
	pthread_mutex_t mut;
	pthread_cond_t cond;
};

mypipe_t* mypipi_init() {
	struct mypipe_st* me;
	me = malloc(sizeof(*me));
	if (me == NULL) {
		return NULL;
	}
	me->head = 0;
	me->tail = 0;
	me->datasize = 0;
	me->count_rd = 0;
	me->count_wr = 0;
	pthread_mutex_init(&me->mut, NULL);
	pthread_cond_init(&me->cond, NULL);

}
static int mypipe_register(mypipe_t* ptr, int opmap) { //注册身份(读者or写者)
	struct mypipe_st* me = ptr;
	pthread_mutex_lock(&me->mut);
	if (opmap & MYPIPE_READ)   //是读者
		me->count_rd++;
	if (opmap & MYPIPE_WRITE) //是写着
		me->count_wr++;
	pthread_cond_broadcast(&me->cond);//叫醒其他等待注册自己身份的人,防止一直wait(下面代码)
	while (me->count_rd <= 0 || me->count_wr <= 0) 
		pthread_cond_wait(&me->cond, &me->mut);
	
	pthread_mutex_unlock(&me->mut);
	return 0;
}
static int mypipe_unregister(mypipe_t* ptr, int opmap) {//注销身份
	struct mypipe_st* me = ptr;
	pthread_mutex_lock(&me->mut);
	if (opmap & MYPIPE_READ)   //是读者
		me->count_rd--;
	if (opmap & MYPIPE_WRITE) //是写着
		me->count_wr--;
	pthread_cond_broadcast(&me->cond);
	pthread_mutex_unlock(&me->mut);
}
static int mypipe_readbyte_unlocked(struct mypipe_st* me, char* datap) {
	if (me->datasize <= 0)		//已经没有数据可读
		return -1;
	*datap = me->data[me->head];
	me->head = (me->head + 1) % PIPESIZE;
	me->datasize--;
	return 0;
}

int mypipe_read(mypipe_t* ptr, void* buf, size_t count) { //真正读了多少数据

	struct mypipe_st* me = ptr;
	int i;
	pthread_mutex_lock(&me->mut);
	while (me->datasize <= 0 && me->count_wr > 0) { //管道没有内容并且有写者

		//pthread_mutex_unlock(&mut_num); //松开 ,让别人尽快放进来
		//sched_yield();
		//pthread_mutex_lock(&mut_num);//加锁
		pthread_cond_wait(&me->cond, &me->mut);

	}

	if (me->datasize <= 0 && me->count_wr <= 0) {
		pthread_mutex_unlock(&me->mut);
		return 0;
	}
	for (i = 0; i < count; i++) {  //每次读一字节

		if (mypipe_readbyte_unlocked(me, buf + i) == -1)//读取失败
			break;
	}
	pthread_cond_broadcast(&me->cond);//通知所有write者,可以写
	pthread_mutex_unlock(&me->mut);
	return i;//返回真正读的数据
}
int mypipe_write(mypipe_t* ptr, const void* buf, size_t size) {	//真正写了多少数据

}
int mypipe_destroy(mypipe_t* ptr) {
	struct mypipe_st* me = ptr;
	pthread_mutex_destroy(&me->mut);
	pthread_cond_destroy(&me->cond);
	free(ptr);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值