十、线程(11章)
$ ps -axm //显示所有的线程
$ ps ax -L //
10.1 线程概念
posix只是线程标准,不是实现。先有标准后有实现。
线程的本质:正在运行的函数,之前的程序都是–程序运行的最小单位。
一个进程相当于一个容器,里面跑了很多个线程。
进程通信是先有实现,后有标准。
线程特点:
每个线程有自己的堆栈和局部变量,但多个线程也可共享同个进程下的所有共享资源
每个线程都有自己的mask,不设置errno值,作为线程函数的返回值返回
线程是异步运行的
10.2 线程创建、终止:
由于pthread库不是标准linux库,所以编译 改为gcc thread.c -lpthread 即可。
pthread_equal()
函数功能:比较线程ID
#include <pthread.h>
int pthread_equal(pthread_t t1, pthread_t t2);
Compile and link with -pthread.
// 不清楚 线程ID 类型,不能直接按照int类型数值的比较方法。
如果两个线程id相等,则pthread_equal()返回一个非零值;否则,返回0。
pthread_self()
函数功能:获取当前线程的线程ID
#include <pthread.h>
pthread_t pthread_self(void);
Compile and link with -pthread.
//返回当前线程的id
pthread_create()
函数功能:创建一个兄弟线程
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
Compile and link with -pthread.
//返回值:成功 --> 0 失败 --> errno number 正数
参数:
- thread:线程的pid,把兄弟线程的标识回填
- attr:线程属性,对于创建出来的兄弟特殊要求
- start_routine:指向函数的指针,把那个函数创建成兄弟线程
- arg:要不要给兄弟线程传参
线程的调度取决于调度器策略,在我们创建的线程还没来得及调度的时候,有可能当前进程就exit(0)结束了。
pthread_exit()
函数功能:正常结束一个线程
线程的终止 的三种方式:
- 线程从启动例程返回,返回值就是线程的退出码
- 线程可以被同一进程中的其他线程取消
- 线程调动 pthread_exit()函数
#include <pthread.h>
void pthread_exit(void *retval);
Compile and link with -pthread.
与return 不同,调用 pthread_exit()结束线程 会实现线程栈的清理。
参数:
- retval:pthread_exit()调用线程的返回值,可由其他函数如pthread_join来检索获取。
pthread_join():
函数功能:线程收尸,谁创建谁收尸,会阻塞等待该线程退出之后再收尸。
int pthread_join(pthread_t thread, void **retval);
Compile and link with -pthread.
参数:
- thread:收尸的兄弟线程的pid
- void **retval 为空,表示 只收尸,不关心状态
代码演示:创建一个兄弟线程,并且对他收尸
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
void *func(void *p)
{
puts("Thread is working.");
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);
}
// void *ptr;
// pthread_join(tid,&ptr);
pthread_join(tid,NULL);
puts("End");
exit(0);
}
/*---------输出----------
xiaolei@xiaolei:~/teacher/example/apue/thread/posix$ ./create
Begin!
Thread is working.
End
*/
执行命令:ps axm -L
3690 - pts/0 - 0:00 ./01.pthread_creat
- 3690 - Sl+ 0:00 - //这里是main线程
- 3691 - Sl+ 0:00 -
3692 - pts/1 - 0:00 ps axm -L
- 3692 - R+ 0:00 -
10.3 栈的清理
回顾:钩子函数 atexit(),在进程正常终止的时候,该函数将会被调用,并逆序调用挂在钩子上面的函数,这里的逆序调用挂载钩子上面的函数这个操作,我们无法介入,一定会被执行。
pthread_cleanup_push()
挂钩子函数
void pthread_cleanup_push(void (*routine)(void *),void *arg);
//参数:挂载的函数---挂载的函数的参数
pthread_cleanup_pop()
用于取下挂在钩子上面的函数,执不执行看参数,相比于 atexit()钩子函数,这里 我们可以自己决定 执行哪个挂载钩子上面的函数,执行起来同样也是逆序。
相当于移除栈顶元素
void pthread_cleanup_pop(int execute);
//决定当前从钩子上面取下来的函数是否被调用。 参数决定是否调用
//execute:0 代表拿了不用,1代表我取下来了并且使用.
两个函数是 宏,两个宏是组合使用,必须成对出现,否则会有语法错误。
代码演示:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
void cleanup_func(void *p)
{
puts(p);
}
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");
puts("push over");
pthread_cleanup_pop(0);
pthread_cleanup_pop(1);
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);
}
/*---------输出----------
xiaolei@xiaolei:~/teacher/example/apue/thread/posix$ ./cleanup
Begin!
Thread is working.
push over
cleanup:2
cleanup:1
End
*/
如果是pthread_cleanup_pop(0); 则不执行,即只弹栈,而不执行对应函数。就算只弹栈,不执行,也一定要写上,有几个push,就一定要有对应的几个 pop,否则会有语法问题。
10.4 线程取消
pthread_cancel()
函数功能: 给一个线程发送一个取消请求。
#include <pthread.h>
int pthread_cancel(pthread_t thread);//取消线程, thread :需要取消的线程ID
Compile and link with -pthread.
为了防止已经申请的资源不能回收,需要设置取消状态,比如,如果需要取消的线程 刚刚open()了一个文件,此时取消,将会导致错过 close(),造成资源泄露。所以需要设置如下 如下状态:
允许取消:
- 异步取消
- 推迟取消,默认是推迟取消, 推迟到 cancel点 再响应
cancel点:POSIX定义的cancel点是指 都是可能引发阻塞的系统调用。
不允许取消:
#include <pthread.h>
int pthread_setcancelstate(int state, int *oldstate); //设置 是否允许取消
int pthread_setcanceltype(int type, int *oldtype); //设置取消方式,异步 或者 推迟
Compile and link with -pthread.
参数:
- state:PTHREAD_CANCEL_ENABLE 和 PTHREAD_CANCEL_DISABLE
- oldstate:
- type:PTHREAD_CANCEL_DEFERRED 和 PTHREAD_CANCEL_ASYNCHRONOUS
pthread_testcancel()
函数功能:应用于没有任何可能引发阻塞的系统调用 的功能函数,即线程中,用于设置取消点,以便取消该线程
#include <pthread.h>
void pthread_testcancel(void); // 本函数什么都不做,就是一个取消点
Compile and link with -pthread.
pthread_detach()
函数功能:分离一个线程,线程分离,如果分离我就不能对它join
#include <pthread.h>
int pthread_detach(pthread_t thread); //thread:线程的pid
Compile and link with -pthread.
一般情况下,对于linux中的资源,我们一直遵循 :谁打开,谁关闭。谁申请,谁释放 这回原则。但是也有特殊情况,假如 创建出某个线程 就不再关心它的生死存亡,就可以分离出去,
注意 已经 pthread_detach()分离的线程,是不能 进行 pthread_join() 收尸的
10.4 线程同步-互斥量
线程间竞争故障:
实验:线程间竞争故障,用sleep()放大竞争故障
”创建20个线程共同操作同一个文件,文件中预设一个整数,如1,完成:打开文件,取数,+1,写回去,关闭文件 “
static void *thr_add(void *p)
{
FILE *fp;
char linebuf[LINESIZE];
fp = fopen(FNAME,"r+");
if(fp == NULL)
{
perror("fopen()");
exit(1);
}
fgets(linebuf,LINESIZE,fp); //这里会发生竞争
fseek(fp,0,SEEK_SET);
sleep(1); //用sleep()放大竞争故障
fprintf(fp,"%d\n",atoi(linebuf)+1);
fclose(fp);
pthread_exit(NULL);
}
int main()
{
int i,err;
pthread_t tid[THRNUM];
for(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(i = 0; i < THRNUM; i++)
pthread_join(tid[i],NULL);
pthread_mutex_destroy(&mut);
exit(0);
}
/*输出
20
19
17
20
*/
由于是虚拟机 单核,所以模拟不出来多核的效果,故 在thr_add() 中 sleep(1) 一秒,比作调度器调度其他线程,放大各个线程之间的竞争,看结果 发现,创建出来的20个线程,全部都拿到了 FNAME文件,并拿到的值都是1,sleep()后 开始加一写值,导致重复写了20次2值到文件中。出现这个问题的原因就是 线程之间的竞争导致的。
互斥量:
注意:对互斥量进行加锁以后,任何其他试图再次对互斥量加锁的线程都会被阻塞,直至当前线程释放该互斥锁
初始化:
#include <pthread.h>
/*初始化和销毁 一个 互斥量*/
//动态初始化
int pthread_mutex_init(pthread_mutex_t *restrict mutexc,const pthread_mutexattr_t *restrict attr);
//attr一般为NULL 表示要用默认的属性初始化互斥量
//销毁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
//静态初始化方式
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
返回值:
成功:0 失败:错误编号
互斥量上锁解锁
作用:保护一段代码以独占的方式在运行
上锁过程:确定临界区,精简临界区,考虑时间,分析是否有跳转,如果有跳转,先解锁再跳转。
注意:每一个不同的变量都要上一个锁
临界区:同一时刻多个人同时运行
//以阻塞的方式 限制住某段代码的执行
int pthread_mutex_lock(pthread_mutex_t *mutex);
//以非阻塞的方式 限制住某段代码的执行
int pthread_mutex_trylock(pthread_mutex_t *mutex);
//解锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);
//mutex:变量的地址
/*
返回值:
成功:0 失败:错误编号
*/
注意:如果互斥量已经上锁(lock),调用线程将阻塞直到互斥量被解锁(unlock)。
可以理解为:只我要拿到了锁,那么当其他线程要这个锁的时候,就拿不到锁,就会阻塞。利用这个规则,制造我们需要的临界区。
代码演示:规定时间内 四个线程 按顺序 分别不停的向终端 输出 a,b,c,d 每个线程输出一个字母
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <string.h>
#define THRNUM 4
static thread_mutex_tp mut[THRNUM];;
int next(int a)
{
if(a + 1 == THRNUM)
return 0;
return a+1;
}
void *thr_func(void *p)
{
int n = (int)p;
char ch = 'a' + n;
while(1)
{
pthread_mutex_lock(mut+n);
write(1, &ch, 1);
pthread_mutex_unlock(mut+next(n));
}
pthread_exit(NULL);
}
int main()
{
int i,err;
pthread_t tid[THRNUM];
for(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);
for(i = 0; i < THRNUM; i++)
pthread_join(tid[i],NULL);
exit(0);
}
/*--------输出---------
xiaolei@xiaolei:~/teacher/example/apue/thread/posix$ ./abcd
abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabc
dabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdab
cdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcda
*/
代码演示:建四个线程,并发实现筛选指数(查询法)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <string.h>
#define NUM 4
#define LEFT 100
#define RIGHT 300
static int job_num = 0;
static int count = 0;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //初始化互斥量1
static pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; //初始化互斥量2
void *primer(void *p)
{
int i,j,flag;
while(1)
{
pthread_mutex_lock(&mutex);
if(job_num == 0)
{
pthread_mutex_unlock(&mutex);
continue;
}
if(job_num == -1)
{
pthread_mutex_unlock(&mutex);
break;
}
i = job_num;
job_num = 0;
pthread_mutex_unlock(&mutex);
flag = 1;
for(j = 2;j < i/2 ;j++)
{
if(i%j == 0)
{
flag = 0;
break;
}
}
if(flag)
{
pthread_mutex_lock(&mutex1);
count++;
printf("[%d]%d is a primer[%d].\n",(int)p,i,count);
pthread_mutex_unlock(&mutex1);
}
}
pthread_exit(NULL);
}
int main()
{
int i,err;
pthread_t tid[NUM];
for(i = 0;i < NUM ;i++)
{
err = pthread_create(tid+i,NULL,primer,(void *)i);
if(err)
{
fprintf(stderr,"pthread_create:%s\n",strerror(err));
exit(1);
}
}
for(i = LEFT;i < RIGHT; i++) //分配任务
{
while(1)
{
pthread_mutex_lock(&mutex);
if(job_num > 0)
{
pthread_mutex_unlock(&mutex);
continue;
}
break;
}
job_num = i;
pthread_mutex_unlock(&mutex);
}
while(1)
{
pthread_mutex_lock(&mutex);
if(job_num != 0)
{
pthread_mutex_unlock(&mutex);
continue;
}
job_num = -1;
pthread_mutex_unlock(&mutex);
break;
}
for(i = 0; i < NUM; i++)
pthread_join(tid[i],NULL);
exit(0);
}
/* 输出
[3]101 is a primer[1].
[3]113 is a primer[2].
[2]103 is a primer[3].
[3]127 is a primer[4].
[1]109 is a primer[5].
[3]137 is a primer[6].
[3]149 is a primer[7].
[3]151 is a primer[8].
[3]157 is a primer[9].
[3]163 is a primer[10].
[3]167 is a primer[11].
[3]173 is a primer[12].
[3]179 is a primer[13].
[3]181 is a primer[14].
[2]131 is a primer[15].
[2]193 is a primer[16].
[2]197 is a primer[17].
[2]199 is a primer[18].
[2]211 is a primer[19].
[2]223 is a primer[20].
[2]227 is a primer[21].
[2]229 is a primer[22].
[2]233 is a primer[23].
[2]239 is a primer[24].
[2]241 is a primer[25].
[2]251 is a primer[26].
[2]257 is a primer[27].
[1]139 is a primer[28].
[1]269 is a primer[29].
[1]271 is a primer[30].
[1]277 is a primer[31].
[1]281 is a primer[32].
[1]283 is a primer[33].
[3]191 is a primer[34].
[0]107 is a primer[35].
[2]263 is a primer[36].
[1]293 is a primer[37].
*/
代码演示:修改上面实验,保证同一个时间,只能一个人做的代码 临界区
“代码演示:创建20个线程共同操作同一个文件,文件中预设一个整数,如1,完成:打开文件,取数,+1,写回去,关闭文件”
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <string.h>
#define THRNUM 20
#define FNAME "tmp.txt"
#define BUFSIZE 128
static pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
void *thr_add(void *p)
{
FILE *fp;
char buf[BUFSIZE];
fp = fopen(FNAME,"r+");
if(fp == NULL)
{
perror("fopen()");
exit(1);
}
pthread_mutex_lock(&mut);
fgets(buf,BUFSIZE,fp);
fseek(fp,0,SEEK_SET);
// sleep(1);
fprintf(fp,"%d\n",atoi(buf)+1);
fclose(fp);
pthread_mutex_unlock(&mut);
pthread_exit(NULL);
}
int main()
{
int i,err;
pthread_t tid[THRNUM];
for(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(i = 0; i < THRNUM; i++)
pthread_join(tid[i],NULL);
pthread_mutex_destroy(&mut);
exit(0);
}
/* 20 */
sched_yield()
相当于一个很短的sleep(),
sched_yield()会让出当前线程的CPU占有权,然后把线程放到静态优先队列的尾端,然后一个新的线程会占用CPU。
sched_yield()这个函数可以使用另一个级别等于或高于当前线程的线程先运行。如果没有符合条件的线程,那么这个函数将会立刻返回然后继续执行当前线程的程序。
而sleep则是等待一定时间后等待CPU的调度,然后去获得CPU资源(这也是usleep()为什么不准的原因)。
有策略的调用sched_yield()能在资源竞争情况很严重时,通过给其他的线程或进程运行机会的方式来提升程序的性能。也就是说,调用sched_yield()能让你的当前线程让出资源,通过一定的策略调用sched_yield()满足你的业务要求可以保证各个线程或进程都有机会运行。
pthread_once
这个模块只能被调用一次(动态模块的单次初始化).
本函数使用初值为PTHREAD_ONCE_INIT的once_control变量保证*init_routine()*函数在本进程执行序列中仅执行一次。
#include <pthread.h>
int pthread_once(pthread_once_t *once_control, void (*init_routine)(void));
10.5 条件变量
引入条变量,来弥补互斥锁的不足
条件变量允许线程阻塞 并 等待其他线程给自己发送信号唤醒自己。兼顾CPU处理时间和效率,这样就弥补了互斥锁的不足。
相关函数:
#include <pthread.h>
//静态初始化条件变量
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
//动态初始化条件变量
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
//唤醒 因为该条件变量cond而阻塞的所有线程中的任意一个线程
int pthread_cond_signal(pthread_cond_t *cond);
//广播形式的唤醒,唤醒所有因 该条件变量cond而阻塞的线程
int pthread_cond_broadcast(pthread_cond_t *cond);
//阻塞等待条件变量变为真,参数:条件变量,互斥量
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
//非阻塞等待条件变量变为真,超时等待
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);
int pthread_cond_destroy(pthread_cond_t *cond);
pthread_cond_wait( )
先解锁,在临界区外等待,等待被唤醒,被唤醒之后会先抢锁,如果有锁,会阻塞,在循环验证
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
用于阻塞线程 并 将线程挂载到 cond条件变量的 阻塞线程队列中,然后解锁,最后等待唤醒。被唤醒之后会抢锁,如果只有自己一个阻塞线程,则被唤醒后 直接持锁。
使用方法(伪码):
pthread _mutex_lock(&mutex)
while或if(线程执行的条件是否成立)
pthread_cond_wait(&cond, &mutex);
线程执行
pthread_mutex_unlock(&mutex);
重点!!!:
问题:pthread_cond_wait() 怎么防止 持锁阻塞,即如果在没有解锁的条件下进入阻塞状态,这一行的话,其他线程将无法访问临界资源。
答:pthread_cond_wait() 内部的实现可以理解为,先将线程挂载到 阻塞队列中,然后解锁。所以在函数内部,已经解锁了。至于函数内部解锁的时机是很重要的,函数是在将 线程挂载到阻塞队列之后 才解锁。这样做的目的是,保证在将线程挂载到阻塞队列之前,其他线程无法改变临界资源。试想一下,如果先解锁,再挂载,会发生什么情况,如果解锁之后,其他线程拿到锁,并改变了临界资源,并发送信号 pthread_cond_signal(), 就会导致 本线程无法响应本次 临界资源的变化。
情景:
线程持锁,并判断条件是否成立,不成立的话,pthread_cond_wait() 阻塞线程 并 将线程挂载到 cond条件变量的 阻塞线程队列中,然后解锁,最后等待唤醒。pthread_cond_wait() 被唤醒之后会抢锁,如果只有自己一个阻塞线程,则被唤醒后 直接持锁, 并开始判断条件是否成立。。。。 成立的话 跳出循环,执行下一步。
pthread_cond_signal() && pthread_cond_broadcast()
能实现通知,但不能实现精准通知。
//阻塞等待条件变量变为真,参数:条件变量,互斥量
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
//非阻塞等待条件变量变为真,超时等待
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);
代码演示:建四个线程,并发实现筛选指数(通知法)
占用系统比较小,不会一直占用系统内存
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <string.h>
#define LEFT 30000000
#define RIGHT 30000200
#define THRNUM 4
static int num = 0;
static pthread_mutex_t mut_num = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond_num = PTHREAD_COND_INITIALIZER;
void *thr_primer(void *p)
{
int i,j,flag;
while(1)
{
pthread_mutex_lock(&mut_num);
while(num == 0)
pthread_cond_wait(&cond_num,&mut_num);
if(num == -1)
{
pthread_mutex_unlock(&mut_num);
break;
}
i = num;
num = 0;
pthread_cond_broadcast(&cond_num);
pthread_mutex_unlock(&mut_num);
flag = 1;
for(j = 2; j < i/2 ;j++)
{
if(i % j == 0)
{
flag = 0;
break;
}
}
if(flag)
printf("[%d]%d is a primer.\n",(int)p,i);
}
pthread_exit(NULL);
}
int main()
{
int i,err;
pthread_t tid[THRNUM];
for(i = 0; i < THRNUM; i++)
{
err = pthread_create(tid+i, NULL, thr_primer,(void *)i);
if(err)
{
fprintf(stderr,"pthread_create():%s\n",strerror(err));
exit(1);
}
}
for(i = LEFT; i <= RIGHT; i++)
{
pthread_mutex_lock(&mut_num);
while(num != 0)
pthread_cond_wait(&cond_num,&mut_num);
num = i;
pthread_cond_signal(&cond_num);
pthread_mutex_unlock(&mut_num);
}
pthread_mutex_lock(&mut_num);
while(num != 0)
pthread_cond_wait(&cond_num,&mut_num);
num = -1;
pthread_cond_broadcast(&cond_num);
pthread_mutex_unlock(&mut_num);
for(i = 0; i < THRNUM; i++)
pthread_join(tid[i],NULL);
pthread_mutex_destroy(&mut_num);
pthread_cond_destroy(&cond_num);
exit(0);
}
创建四个互斥锁,四个线程,临界区代码中,每个线程负责持锁,输出,并释放下一个线程的锁。这样 每个线程就可以轮询持锁输出。
”代码演示:用 条件变量 通知法 实现 :规定时间内 四个线程 按顺序 分别不停的向终端 输出 a,b,c,d 每个线程输出一个字母“
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <string.h>
#define THRNUM 4
static int num = 0;
static pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int next(int a)
{
if(a + 1 == THRNUM)
return 0;
return a+1;
}
void *thr_func(void *p)
{
int n = (int)p;
char ch = 'a' + n;
while(1)
{
pthread_mutex_lock(&mut);
while(num != n)
pthread_cond_wait(&cond,&mut);
write(1, &ch, 1);
num = next(num);
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mut);
}
pthread_exit(NULL);
}
int main()
{
int i,err;
pthread_t tid[THRNUM];
for(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);
for(i = 0; i < THRNUM; i++)
pthread_join(tid[i],NULL);
exit(0);
}
/*--------输出---------
xiaolei@xiaolei:~/teacher/example/apue/thread/posix$ ./abcd
abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabc
dabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdab
cdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcda
*/
代码演示:两个线程,一个线程打印数字123456,一个线程打印字母ABCDEF,交替打印出12A34B56C.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
static int count_num = 52; //打印次数
static int flag = 0;
static pthread_mutex_t mut1 = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond_num1 = PTHREAD_COND_INITIALIZER;
void *pthread_num(void *p)
{
int num = 1;
for(num ;num <= count_num;num++)
{
pthread_mutex_lock(&mut1);
while(flag != 0)
pthread_cond_wait(&cond_num1,&mut1);
if(flag == 0)
{
printf("%d",num);
fflush(NULL);
printf("%d",++num); // 12 34 56
fflush(NULL);
}
flag = 1;
pthread_cond_broadcast(&cond_num1);
pthread_mutex_unlock(&mut1);
}
pthread_exit(0);
}
void *pthread_ABCD(void *p)
{
char abcd;
for(abcd = 'A' ;abcd <= 'Z';abcd++)
{
pthread_mutex_lock(&mut1);
while(flag != 1)
pthread_cond_wait(&cond_num1,&mut1);
if(flag == 1)
{
printf(" %c ",abcd); // A B C D
fflush(NULL);
}
flag = 0;
pthread_cond_broadcast(&cond_num1);
pthread_mutex_unlock(&mut1);
}
pthread_exit(0);
}
int main()
{
int i,err1,err2;
pthread_t tid[2];
/*if error*/
err1 = pthread_create(tid+0,NULL,pthread_num,NULL);
err2 = pthread_create(tid+1,NULL,pthread_ABCD,NULL);
pthread_join(tid[0],NULL);
pthread_join(tid[1],NULL);
printf("\n");
exit(0);
}
/* 输出
12 A 34 B 56 C 78 D 910 E 1112 F 1314 G 1516 H 1718 I 1920 J 2122 K 2324 L 2526 M 2728 N 2930 O 3132 P 3334 Q 3536 R 3738 S 3940 T 4142 U 4344 V 4546 W 4748 X 4950 Y 5152 Z
*/