linux系统编程04-并发:线程
介绍
1. 线程的概念
会话→进程组→进程→线程:线程已成为编写程序的基本单位,其他都是容器
- 一个正在运行的函数,没有主次之分
- 先有标准,后有实现:Posix线程是一套标准,而不是实现
- 线程标识: pthread_t 【整型/结构体/共用体,不同标准实现方式不同,posix是整型】
ps axm
:查看more,看到线程
ps ax -L
:以linux方式查看,可以发现线程是占用进程号的,即也是用进程号标识
#include <pthread.h>
//比较两个线程id的大小
int pthread_equal(pthread_t t1, pthread_t t2);
//返回当前线程的id,【类进程的getpid()】
pthread_t pthread_self(void);
**Compile and link with -pthread.**
makefile写法:
CFLAGS+=-pthread
LDFLAGS+=-pthread
2. 进程的基本行为
创建:pthread_create
线程的调度也是取决于调取器的策略
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
- 参数:
thread
:线程号,回填attr
:线程的属性,一般默认start_routine
:线程跑的函数(线程就是一个正在运行的函数)arg
:新线程需要的参数
- 返回值:
- 成功:0
- 失败:返回errno,用
strerror
报错
设置errno | 返回errno | 报错方式 |
---|---|---|
否 | 否 | fprintf |
是 | 否 | perror |
否 | 是 | strerror |
例子:创建线程
示例代码:
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<string.h>
static void* func(void *p)
{
puts("Thread is working!");
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);
}
puts("End!");
exit(0);
}
运行结果:
结果分析:
刚创建的线程可能来不及调度,main线程就调用了 exit(0)
,导致整个进程正常终止,回收了所有线程,所以刚创建的线程来不及打印 “Thread is working!”
终止:pthread_exit、pthread_join
进程终止方式:Untitled
1)3):正常终止; 2):异常终止
#include <pthread.h>
//线程程终止
void pthread_exit(void *retval);
//线程收尸
int pthread_join(pthread_t thread, void **retval);
示例代码:
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<string.h>
static void* func(void *p)
{
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);
}
pthread_join(tid,NULL);
puts("End!");
exit(0);
}
运行结果:
对比:
进程 | 线程 |
---|---|
fork() |
pthread_create() |
exec() |
pthread_create() |
wait() |
pthread_join() |
exit() |
pthread_exit() |
exit
和 return
的区别,是否做善后,如调用钩子函数/清理线程栈
清理:pthread_cleanup
像钩子函数,在执行 pexit
之前会先被执行
#include <pthread.h>
void pthread_cleanup_push(void (*routine)(void *),
void *arg);
void pthread_cleanup_pop(int execute);
-
实际上是宏
push的另外一个大括号在pop中,必须成对使用,否则报语法错可以执行不到,默认为1,但一定要有
示例代码:
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<string.h>
static void cleanup_func(void* p)
{
puts(p);
}
static void* func(void *p)
{
puts("Thread is working!");
pthread_cleanup_push(cleanup_func, "cleanup:1");
pthread_cleanup_push(cleanup_func, "cleanup:2");
pthread_cleanup_push(cleanup_func, "cleanup:3");
pthread_cleanup_pop(1);
pthread_cleanup_pop(0);
pthread_cleanup_pop(1);
pthread_exit(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);
}
运行结果:
取消:pthread_cancel
很常用,比如搜索一个二叉树,四个线程搜索四棵子树,其中一棵找到其他线程就能停了,取消然后收尸
pthread_cancel
使某个线程终止,默认是允许在cancel点终止,Posix中cancel是阻塞的系统调用。
cancel实际上不会在 cleanup取消,因为不是cancel点,open是
线程竞争实例:筛质数
例子:筛素数,每个线程处理一个数
E1:有参数冲突
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<string.h>
##define LEFT 30000000
##define RIGHT 30000200
##define THRDNUM (RIGHT-LEFT+1)
static void* thr_prim(void *p);
int main()
{
int i,err;
pthread_t tid[THRDNUM];
for(i = LEFT; i <= RIGHT; i++)
{
err = pthread_create(tid+i-LEFT, NULL, thr_prim, &i);
if(err)
{
fprintf(stderr, "thread_create():%s\n", strerror(err));
exit(1);
}
}
for(i = LEFT; i <= RIGHT; i++)
pthread_join(tid[i-LEFT],NULL);
exit(0);
}
static void *thr_prim(void *p)
{
int i,j;
int mark = 1;
i = *(int *)p;
for(j = 2; j < i/2; j++)
{
if(i%j == 0)
{
mark = 0;
break;
}
}
if(mark)
printf("%d is a primer!\n", i);
//sleep(1000);
pthread_exit(NULL);
}
运行结果:
问题:
线程创建的时候传的是地址 &i
,相当于 THRDNUM
个线程都在竞争这一个地址上的数据
E2:解决参数冲突
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<string.h>
##define LEFT 30000000
##define RIGHT 30000200
##define THRDNUM (RIGHT-LEFT+1)
struct thr_arg_st
{
int n;
};
static void* thr_prim(void *p);
int main()
{
int i,err;
pthread_t tid[THRDNUM];
struct thr_arg_st *p;
void *ptr;
for(i = LEFT; i <= RIGHT; i++)
{
//做成结构体传参,malloc申请空间,防止冲突
p = malloc(sizeof(struct thr_arg_st));
p->n = i;
err = pthread_create(tid+i-LEFT, NULL, thr_prim, p);
if(err)
{
fprintf(stderr, "thread_create():%s\n", strerror(err));
exit(1);
}
}
for(i = LEFT; i <= RIGHT; i++)
{
//利用返回值free
pthread_join(tid[i-LEFT],&ptr);
free(ptr);
}
exit(0);
}
static void *thr_prim(void *p)
{
int i,j;
int mark = 1;
i = ((struct thr_arg_st *)p)->n;
for(j = 2; j < i/2; j++)
{
if(i%j == 0)
{
mark = 0;
break;
}
}
if(mark)
printf("%d is a primer!\n", i);
//把malloc的地址传回去释放
pthread_exit(p);
}
运行结果:
3. 线程的同步:互斥量、条件变量
(1)互斥量:pthread_mutex_init
安装 posix
man手册: sudo apt-get install manpages-posix-dev
- 锁的是临界区(代码),而不是资源本身
- lock 是 死等,阻塞, trylock 是非阻塞
例子:创建20个线程,分别读取同一个文件,然后+1放回
示例代码:
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<string.h>
#include<unistd.h>
##define THRDNUM 20
##define FILENAME "/tmp/out"
##define LINESIZE 1024
//创建互斥量:静态初始化
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static void* thr_add(void *p)
{
FILE *fp;
char linebuf[LINESIZE];
fp = fopen(FILENAME, "r+");
if(fp == NULL)
{
perror("fopen()");
exit(1);
}
//进入临界区
pthread_mutex_lock(&mutex);
fgets(linebuf, LINESIZE, fp);
fseek(fp, 0, SEEK_SET);
//sleep(1); //有冲突则会强化
fprintf(fp, "%d\n",atoi(linebuf)+1);
fclose(fp);
//出临界区
pthread_mutex_unlock(&mutex);
pthread_exit(NULL);
}
int main()
{
pthread_t tid[THRDNUM];
int i,err;
for(i = 0; i < THRDNUM; i++)
{
err = pthread_create(tid+i, NULL, thr_add, NULL);
if(err)
{
fprintf(stderr, "pthread_create():%s\n",strerror(err));
exit(1);
}
}
for(i = 0; i < THRDNUM; i++)
pthread_join(tid[i], NULL);
//销毁信号量
pthread_mutex_destroy(&mutex);
exit(0);
}
运行结果:
注意点:读完后,要覆盖写掉读的内容,移动文件指针,如果不移:
一直读1,写2,写了20次
例子:创建四个线程,彼此同步,打印 “abcd”
示例代码:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>
##define THRDNUM 4
pthread_mutex_t mutex_arr[THRDNUM];
static void* thr_func(void* p)
{
int n = (int)p;
int c = 'a'+n;
while(1)
{
pthread_mutex_lock(mutex_arr+n);
write(1,&c,1);
pthread_mutex_unlock(mutex_arr+(n+1)%THRDNUM);
}
pthread_exit(NULL);
}
int main()
{
pthread_t tid[THRDNUM];
int i, err;
for(i = 0; i < THRDNUM; i++)
{
pthread_mutex_init(mutex_arr+i,NULL);
pthread_mutex_lock(mutex_arr+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(mutex_arr+0);
alarm(3);
for(i = 0; i < THRDNUM; i++)
{
pthread_join(tid[i], NULL);
}
exit(0);
}
运行结果:
思路:
刚创建时加锁,使得创建出的四个线程进入 while
后阻塞在锁上,main线程 解开 thread1 的锁,然后开始线程同步。
E3:筛质数池类写法
非正规,任务池
main线程下发任务,其他线程处理任务,彼此间通过全局变量 num
通信
num > 0
发放任务num == 0
任务以分配,main还未下达num == -1
退出
示例代码:
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<string.h>
##define LEFT 30000000
##define RIGHT 30000200
##define THRDNUM 4
static int num = 0;
static pthread_mutex_t mutex_num = PTHREAD_MUTEX_INITIALIZER;
static void* thr_prim(void *p);
int main()
{
int i,err;
pthread_t tid[THRDNUM];
//创建线程
for(i = 0; i < THRDNUM; i++)
{
err = pthread_create(tid+i, NULL, thr_prim, (void*)i);
if(err)
{
fprintf(stderr, "thread_create():%s\n", strerror(err));
exit(1);
}
}
//下发任务
for(i = LEFT; i <= RIGHT; i++)
{
pthread_mutex_lock(&mutex_num);
while(num != 0)
{
pthread_mutex_unlock(&mutex_num);
sched_yield();
pthread_mutex_lock(&mutex_num);
}
num = i;
pthread_mutex_unlock(&mutex_num);
}
//结束任务
pthread_mutex_lock(&mutex_num);
while(num != 0)
{
pthread_mutex_unlock(&mutex_num);
sched_yield();
pthread_mutex_lock(&mutex_num);
}
num = -1;
pthread_mutex_unlock(&mutex_num);
for(i = 0; i < THRDNUM; i++)
pthread_join(tid[i],NULL);
pthread_mutex_destroy(&mutex_num);
exit(0);
}
static void *thr_prim(void *p)
{
int i,j;
while(1)
{
int mark = 1;
pthread_mutex_lock(&mutex_num);
while(num == 0)
{
pthread_mutex_unlock(&mutex_num);
sched_yield();
pthread_mutex_lock(&mutex_num);
}
if(num == -1)
{
//警惕所有跳出临界区的跳转语句,可能导致死锁
pthread_mutex_unlock(&mutex_num);
break;
}
i = num;
num = 0;
pthread_mutex_unlock(&mutex_num);
for(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(0);
}
运行结果:
注意:
经典死锁:临界区跳转,break, continue, function calling, goTo, long jump
问题:
上下游都忙等,cpu利用率高:上游 main
分配任务后, num
变为0,可是 main
抢不到锁;如果 main
抢不到锁, num==0
,下游线程又会一直强锁检查,形成恶行循环。一切都取决于调度器能否调度到 main
。或许可以采用通知法的改进,解除忙等。
互斥量令牌桶
修改makefile
CFLAGS+=-pthread
LDFLAGS+=-pthread
all:mytbf
mytbf:main.o mytbf.o
gcc $^ -o $@ $(CFLAGS) $(LDFLAGS)
clean:
rm -rf *.o mytbf
示例代码:
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>
#include<string.h>
#include<errno.h>
#include<pthread.h>
#include "mytbf.h"
struct mytbf_t
{
int cps;
int burst;
int token;
int pos;
//局部互斥量:保护自己的token读写
pthread_mutex_t mut;
};
//typedef void (*sighandler_t)(int);
//全局互斥量:保护job数组
static struct mytbf_t* job [MYTBF_MAX];
static pthread_mutex_t mut_job = PTHREAD_MUTEX_INITIALIZER;
static pthread_t tid_alrm;
static pthread_once_t init_once = PTHREAD_ONCE_INIT;
static int inited = 0;
//static sighandler_t alarm_handler_save;
static void* thr_alrm(void *p)
{
int i;
//alarm(1);
while(1)
{
pthread_mutex_lock(&mut_job);
for(i = 0; i < MYTBF_MAX; i++)
{
if(job[i] != NULL)
{
pthread_mutex_lock(&job[i]->mut);
job[i]->token += job[i]->cps;
if(job[i]->token > job[i]->cps)
job[i]->token = job[i]->cps;
pthread_mutex_unlock(&job[i]->mut);
}
}
pthread_mutex_unlock(&mut_job);
sleep(1);
}
}
//模块卸载:在进程结束的时候调用,钩子函数
static void module_unload(void)
{
int i;
//signal(SIGALRM, alarm_handler_save);
//alarm(0);
//回收线程
pthread_cancel(tid_alrm);
pthread_join(tid_alrm, NULL);
for(i = 0; i < MYTBF_MAX; i++)
if(job[i]!=NULL)
mytbf_destroy(job[i]);
pthread_mutex_destroy(&mut_job);
}
static void module_load(void)
{
//alarm_handler_save = signal(SIGALRM, alarm_handler);
//alarm(1);
int err;
err = pthread_create(&tid_alrm, NULL, thr_alrm, NULL);
if(err)
{
fprintf(stderr,"pthread_create():%s\n",strerror(err));
exit(1);
}
//挂钩
atexit(module_unload);
}
static int get_free_pos_unlocked(void )
{
int i;
for(i = 0; i < MYTBF_MAX; i++)
{
if(job[i] == NULL)
return i;
}
return -1;
}
mytbf_t *mytbf_init(int cps, int burst)
{
struct mytbf_t *me;
int pos;
/*lock
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;
me->pos = pos;
pthread_mutex_init(&me->mut,NULL);
//临界区:注意跳转语句
pthread_mutex_lock(&mut_job);
pos = get_free_pos_unlocked();
if(pos < 0)
{
pthread_mutex_unlock(&mut_job);
free(me);
return NULL;
}
job[pos] = me;
pthread_mutex_unlock(&mut_job);
return me;
}
static int min(int a, int b)
{
if(a < b)
return a;
else
return b;
}
int mytbf_fetchtoken(mytbf_t *ptr, int size)
{
struct mytbf_t *me = ptr;
int n;
if(size < 0)
return -EINVAL;
//私有信号量保护:临界区
pthread_mutex_lock(&me->mut);
//这里存在忙等
while(me->token <= 0)
{
pthread_mutex_unlock(&me->mut);
sched_yield();
pthread_mutex_lock(&me->mut);
}
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_t *me = ptr;
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_t *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;
}
运行结果:
存在问题:
fetchtoken
仍是查询法,存在忙等
(2)条件变量:pthread_cond_init
思路:mutex+队列,用来解决互斥量的忙等现象,和互斥量配合使用
-
加锁,在connd上等待,解锁
-
经典用法:
mutex+cond = semphor
pthread_cond_wait
:先解锁后等待,被唤醒后再加锁查看内容
用 while
是为了防止复用 cond
的时候,无关线程被误唤醒
条件变量令牌桶
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>
#include<string.h>
#include<errno.h>
#include<pthread.h>
#include "mytbf.h"
struct mytbf_t
{
int cps;
int burst;
int token;
int pos;
//局部互斥锁:用来实现条件变量
pthread_mutex_t mut;
//条件变量:保护自己的token读写
pthread_cond_t cond;
};
//全局互斥量:保护job数组
static struct mytbf_t* job [MYTBF_MAX];
static pthread_mutex_t mut_job = PTHREAD_MUTEX_INITIALIZER;
static pthread_t tid_alrm;
static pthread_once_t init_once = PTHREAD_ONCE_INIT;
static int inited = 0;
static void* thr_alrm(void *p)
{
int i;
while(1)
{
pthread_mutex_lock(&mut_job);
for(i = 0; i < MYTBF_MAX; i++)
{
if(job[i] != NULL)
{
pthread_mutex_lock(&job[i]->mut);
job[i]->token += job[i]->cps;
if(job[i]->token > job[i]->cps)
job[i]->token = job[i]->cps;
//唤醒阻塞在条件变量上的线程
pthread_cond_broadcast(&job[i]->cond);
pthread_mutex_unlock(&job[i]->mut);
}
}
pthread_mutex_unlock(&mut_job);
sleep(1);
}
}
//模块卸载:在进程结束的时候调用,钩子函数
static void module_unload(void)
{
int i;
//回收线程
pthread_cancel(tid_alrm);
pthread_join(tid_alrm, NULL);
for(i = 0; i < MYTBF_MAX; i++)
if(job[i]!=NULL)
mytbf_destroy(job[i]);
pthread_mutex_destroy(&mut_job);
}
static void module_load(void)
{
int err;
err = pthread_create(&tid_alrm, NULL, thr_alrm, NULL);
if(err)
{
fprintf(stderr,"pthread_create():%s\n",strerror(err));
exit(1);
}
//挂钩
atexit(module_unload);
}
static int get_free_pos_unlocked(void )
{
int i;
for(i = 0; i < MYTBF_MAX; i++)
{
if(job[i] == NULL)
return i;
}
return -1;
}
mytbf_t *mytbf_init(int cps, int burst)
{
struct mytbf_t *me;
int pos;
/*lock
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;
me->pos = pos;
pthread_mutex_init(&me->mut,NULL);
pthread_cond_init(&me->cond,NULL);
//临界区:注意跳转语句
pthread_mutex_lock(&mut_job);
pos = get_free_pos_unlocked();
if(pos < 0)
{
pthread_mutex_unlock(&mut_job);
free(me);
return NULL;
}
job[pos] = me;
pthread_mutex_unlock(&mut_job);
return me;
}
static int min(int a, int b)
{
if(a < b)
return a;
else
return b;
}
int mytbf_fetchtoken(mytbf_t *ptr, int size)
{
struct mytbf_t *me = ptr;
int n;
if(size < 0)
return -EINVAL;
//私有互斥量保护:临界区
pthread_mutex_lock(&me->mut);
//条件变量避免忙等
while(me->token <= 0)
{
pthread_cond_wait(&me->cond, &me->mut);
/*
pthread_mutex_unlock(&me->mut);
sched_yield();
pthread_mutex_lock(&me->mut);*/
}
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_t *me = ptr;
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);//并发共用信号量时,你返回的token可能够别人申请的token
pthread_mutex_unlock(&me->mut);
return size;
}
int mytbf_destroy(mytbf_t *ptr)
{
struct mytbf_t *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;
}
fetchtoken()
:不忙等
思路:加入cond,忙等的死循环用wait替代,在适当的地方(等待x就在改变x的地方)唤醒
条件变量筛质数
非忙等
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<string.h>
##define LEFT 30000000
##define RIGHT 30000200
##define THRDNUM 4
static int num = 0;
static pthread_mutex_t mutex_num = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond_num = PTHREAD_COND_INITIALIZER;
static void* thr_prim(void *p);
int main()
{
int i,err;
pthread_t tid[THRDNUM];
//创建线程
for(i = 0; i < THRDNUM; i++)
{
err = pthread_create(tid+i, NULL, thr_prim, (void*)i);
if(err)
{
fprintf(stderr, "thread_create():%s\n", strerror(err));
exit(1);
}
}
//下发任务
for(i = LEFT; i <= RIGHT; i++)
{
pthread_mutex_lock(&mutex_num);
while(num != 0)//在num=0的地方broadcast
{
pthread_cond_wait(&cond_num, &mutex_num);
/*
pthread_mutex_unlock(&mutex_num);
sched_yield();
pthread_mutex_lock(&mutex_num);*/
}
num = i;
pthread_cond_signal(&cond_num);
pthread_mutex_unlock(&mutex_num);
}
//结束任务
pthread_mutex_lock(&mutex_num);
while(num != 0)
{
pthread_mutex_unlock(&mutex_num);
sched_yield();
pthread_mutex_lock(&mutex_num);
}
num = -1;
pthread_cond_broadcast(&cond_num);
pthread_mutex_unlock(&mutex_num);
for(i = 0; i < THRDNUM; i++)
pthread_join(tid[i],NULL);
pthread_mutex_destroy(&mutex_num);
exit(0);
}
static void *thr_prim(void *p)
{
int i,j;
while(1)
{
int mark = 1;
pthread_mutex_lock(&mutex_num);
while(num == 0)//在使num!=0的地方叫醒
{
pthread_cond_wait(&cond_num, &mutex_num);
/*
pthread_mutex_unlock(&mutex_num);
sched_yield();
pthread_mutex_lock(&mutex_num);*/
}
if(num == -1)
{
//警惕所有跳出临界区的跳转语句,可能导致死锁
pthread_mutex_unlock(&mutex_num);
break;
}
i = num;
num = 0;
pthread_cond_broadcast(&cond_num);
pthread_mutex_unlock(&mutex_num);
for(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(0);
}
条件变量实现abcd
创建四个线程,彼此同步,打印 “abcd”
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>
##define THRDNUM 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;
int c = 'a'+n;
while(1)
{
pthread_mutex_lock(&mut);
while(num != n)
pthread_cond_wait(&cond, &mut);
write(1,&c,1);
num = (num+1)%THRDNUM;
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mut);
}
pthread_exit(NULL);
}
int main()
{
pthread_t tid[THRDNUM];
int i, err;
for(i = 0; i < THRDNUM; i++)
{
err = pthread_create(tid+i, NULL, thr_func, (void*)i);
if(err)
{
fprintf(stderr, "pthread_create():%s\n", strerror(err));
exit(1);
}
}
alarm(3);
for(i = 0; i < THRDNUM; i++)
{
pthread_join(tid[i], NULL);
}
pthread_mutex_destroy(&mut);
pthread_cond_destroy(&cond);
exit(0);
}
(3)信号量:自主实现
方法 | 构成 |
---|---|
互斥量 | 锁 |
条件变量 | 锁+队列 |
信号量 | int+队列 |
自己实现一个信号量并且封装成库
用封装的库修改筛素数E2,用一批一批的线程计算,每批4个
示例代码:
CFLAGS+=-pthread
LDFLAGS+=-pthread
all:mysem
mysem:main.o mysem.o
gcc $^ -o $@ $(CFLAGS) $(LDFLAGS)
clean:
rm -rf *.o mysem
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<string.h>
#include<unistd.h>
#include"mysem.h"
##define LEFT 30000000
##define RIGHT 30000200
##define THRDNUM (RIGHT-LEFT+1)
##define N 4
//创建信号量
static mysem_st *sem;
static void* thr_prim(void *p);
int main()
{
int i,err;
pthread_t tid[THRDNUM];
sem = mysem_init(N);
if(sem == NULL)
{
fprintf(stderr,"mysem_init() failed\n");
exit(1);
}
for(i = LEFT; i <= RIGHT; i++)
{
//减资源量,减不动就阻塞在这里
mysem_sub(sem, 1);
err = pthread_create(tid+i-LEFT, NULL, thr_prim, (void*)i);
if(err)
{
fprintf(stderr, "thread_create():%s\n", strerror(err));
exit(1);
}
}
for(i = LEFT; i <= RIGHT; i++)
pthread_join(tid[i-LEFT],NULL);
//销毁信号量
mysem_destroy(sem);
exit(0);
}
static void *thr_prim(void *p)
{
int i,j;
int mark = 1;
i = (int)p;
for(j = 2; j < i/2; j++)
{
if(i%j == 0)
{
mark = 0;
break;
}
}
if(mark)
printf("%d is a primer!\n", i);
// sleep(5);
//加资源量,会唤醒
mysem_add(sem,1);
pthread_exit(NULL);
}
##ifndef MYSEM_H__
##define MYSEM_H__
typedef void mysem_st;
mysem_st *mysem_init(int initval);
int mysem_add(mysem_st *, int);
int mysem_sub(mysem_st *, int);
int mysem_destroy(mysem_st *);
##endif
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include"mysem.h"
struct mysem_st
{
int value;
pthread_mutex_t mut;
pthread_cond_t cond;
};
mysem_st *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_add(mysem_st *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_sub(mysem_st *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_destroy(mysem_st *ptr)
{
struct mysem_st *me = ptr;
pthread_mutex_destroy(&me->mut);
pthread_cond_destroy(&me->cond);
free(me);
return 0;
}
运行结果:
main,c
中系上测试用的 sleep(5)
语句, 用 ps ax -L
即可查看每批四个线程的变化
拓展:可以变成信号量数组,支持多个信号量,类似于 mytbf
(很实用的库模板)
读写锁:
- 读锁:共享锁
- 写锁:互斥锁
4. 线程属性
可以设置多少个线程:栈空间/线程占用空间;但是在此之前pid会先被耗尽,所以是受限于pid
-
示例代码:
#include<stdio.h> #include<stdlib.h> #include<unistd.h> int main() { int i=0; pid_t pid; for(i=0; ;i++) { pid = fork(); if(pid < 0) { perror("fork()"); break; } if(pid == 0) { sleep(3); exit(0); } } printf("process num:%d\n",i); exit(0); }
#include<stdio.h> #include<stdlib.h> #include<pthread.h> #include<string.h> #include<unistd.h> static void* thr_func(void *p) { while(1) pause(); pthread_exit(0); } int main() { pthread_t tid; int err, i; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setstacksize(&attr,1024*1024); for(i=0; ;i++) { err = pthread_create(&tid, &attr, thr_func, NULL); if(err) { fprintf(stderr,"pthread_create():%s\n",strerror(err)); break; } } printf("thread number : %d\n",i); pthread_attr_destroy(&attr); exit(0); }
5.重入
线程的几种工作模式
流水线、分治、C/S
6. openMP
gcc支持的一种跨语言的语法标记,实现多线程
CFLAGS+=-Wall -fopenmp
几个核则几个线程并发
#include<stdio.h>
#include<stdlib.h>
int main()
{
##pragma omp parallel
{
puts("Hello");
puts("World");
exit(0);
}
}
分section并发,同样取决于几核
#include<stdio.h>
#include<stdlib.h>
#include<omp.h>
int main()
{
##pragma omp parallel sections
{
##pragma omp section
printf("[%d]Hello\n",omp_get_thread_num());
##pragma omp section
printf("[%d]World\n",omp_get_thread_num());
}
exit(0);
}