十、线程(11 章)

本文详细介绍了线程的概念、创建与终止,重点讲解了线程栈清理、线程取消、互斥量和条件变量在多线程同步中的应用,通过实例展示了如何避免线程竞争故障,以及如何实现线程间的有序协作。此外,还探讨了调度器函数和一次性执行函数pthread_once的作用。
摘要由CSDN通过智能技术生成

十、线程(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_ENABLEPTHREAD_CANCEL_DISABLE
  • oldstate:
  • type:PTHREAD_CANCEL_DEFERREDPTHREAD_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_INITonce_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)

whileif(线程执行的条件是否成立)
	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 
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值