POSIX多线程程序设计_流水线工作例程

#include<pthread.h>
#include "errors.h"

typedef struct stage_tag{<span style="white-space:pre">			</span>//流水线的工作单元结构体
	pthread_mutex_t 	mutex;<span style="white-space:pre">		</span>//保护当前工作单元数据的互斥锁
	pthread_cond_t 		avail;<span style="white-space:pre">	</span>//等待当前工作单元存储数据可用的条件变量
	pthread_cond_t 		ready;<span style="white-space:pre">	</span>//等待当前工作单元可处理新数据的条件变量
	int 			data_ready;<span style="white-space:pre">		</span>//表示当前工作单元存放数据的状态(0表示data为过期数据,1表示data为有效数据)
	long 			data;<span style="white-space:pre">		</span>//当前工作单元存放的数据
	pthread_t 		thread;<span style="white-space:pre">		</span>//当前工作单元所在的线程ID
	struct stage_tag 	*next;<span style="white-space:pre">		</span>//流水线中指向下一工作单单元的指针
}stage_t;

typedef struct pipe_tag{<span style="white-space:pre">			</span>//流水线结构体
	pthread_mutex_t	mutex;<span style="white-space:pre">		</span>//保护流水线加入新数据或读出流水线工作结果的数据
	stage_t		*head;<span style="white-space:pre">			</span>//流水线的第一个工作单原指针
	stage_t		*tail;<span style="white-space:pre">			</span>//流水线的最后一个工作单原的指针(保存数据经过流水线处理之后所得的结果数据)
	int 		stages;<span style="white-space:pre">				</span>//流水线中工作单元的数量
	int 		active;<span style="white-space:pre">				</span>//流水线中正在处理数据的工作单元的数量
}pipe_t;

int pipe_send(stage_t *stage,long data){<span style="white-space:pre">							</span>//向stage所指向的工作单原传送新数据data的函数
	int status;<span style="white-space:pre">	</span>//保存线程函数的调用状态(返回值为0表示调用成功,否则调用失败就打印错误信息)
	status = pthread_mutex_lock(&stage->mutex);<span style="white-space:pre">					</span>//试图锁定stage指向的工作单原的互斥锁
	if(status != 0)
		return status;
	while(stage->data_ready){<span style="white-space:pre">									</span>//等待条件变量ready发送当前工作单原可处理新数据的信号
		status = pthread_cond_wait(&stage->ready,&stage->mutex); //<span style="font-family: Arial, Helvetica, sans-serif;">如果data_ready为1,即当前工作单原存储的数据为有效数据则等待处理完毕</span>
		if(status != 0){<span style="white-space:pre">											</span>//<span style="font-family: Arial, Helvetica, sans-serif;">否则,表示当前工作单元存储的数据为过期数据,可以处理新传入的数据data</span>
			pthread_mutex_unlock(&stage->mutex);
			return status;
		}
	}
	stage->data = data;<span style="white-space:pre">										</span>//更新当前工作单原存储的数据为data
	stage->data_ready = 1;<span style="white-space:pre">										</span>//更新后的数据为有效数据,data_ready设置为1
	status = pthread_cond_signal(&stage->avail);<span style="white-space:pre">					</span>//并且发送avail信号通知等待中的线程该工作单元的数据为可用的有效数据
	if(status != 0){
		pthread_mutex_unlock(&stage->mutex);
		return status;
	}
	status = pthread_mutex_unlock(&stage->mutex);<span style="white-space:pre">				</span>//解锁当前工作单元的互斥锁
	return status;<span style="white-space:pre">												</span>//返回函数调用状态
}

void *pipe_stage(void *arg){<span style="white-space:pre">									</span>//线程入口函数,arg为当前的工作单元结构体的指针
	stage_t *stage = (stage_t*)arg;<span style="white-space:pre">								</span>//j将arg转换成stage_t*类型并赋值给stage
	stage_t *next_stage = stage->next;<span style="white-space:pre">							</span>//将stage的下一个工作单元的指针赋值给stage_next
	int status;<span style="white-space:pre">		</span>//同上的status

	status = pthread_mutex_lock(&stage->mutex);<span style="white-space:pre">					</span>//开始工作,试图锁定当前互斥锁
	if(status != 0)
		err_abort(status,"Lock pipe stage");
	while(true){<span style="white-space:pre">												</span>//循环处理当前工作单原内的数据
		while(stage->data_ready != 1){<span style="white-space:pre">							</span>//等待条件变量avail发送当前数据可用的信号
			status = pthread_cond_wait(&stage->avail,&stage->mutex);//等待的开始时会先解锁绑定的互斥锁
			if(status != 0)
				err_abort(status,"Wait for previous stage");
		}
		pipe_send(next_stage, stage->data + 1);<span style="white-space:pre">					</span>//将对当前数据工作(这里是对data+1)后的结果发送给下一个工作单元处理
		stage->data_ready = 0;<span style="white-space:pre">									</span>//数据传送给下一个工作单元后当前数矩即过期,data_ready设置为0
		status = pthread_cond_signal(&stage->ready);<span style="white-space:pre">				</span>//并发送ready信号通知等待中的线程,该工作单元可以接收并处理新的数据
		if(status != 0)
			err_abort(status,"Wake next stage");
	}
}

int pipe_create(pipe_t * pipe,int stages){<span style="white-space:pre">							</span>//创建工作流的函数 stages表示工作单元数量
	int pipe_index;<span style="white-space:pre">		</span>//当前创建的工作单原的索引
	stage_t **link = &pipe->head, *new_stage, *stage;//用于创建链表的各个指针变量
	int status;<span style="white-space:pre">	</span>//同上的status

	status = pthread_mutex_init(&pipe->mutex,NULL);<span style="white-space:pre">				</span>//初始化保护工作流链表的互斥锁
	if(status != 0)
		err_abort(status , "Init pipe mutex");
	pipe->stages = stages;
	pipe->active = 0;<span style="white-space:pre">											</span>//当前正在处理数据的工作单元数初始化为0

	for(pipe_index = 0; pipe_index <= stages; pipe_index++){<span style="white-space:pre">			</span>//循环创建工作单元的数据节点
		new_stage = (stage_t*)malloc(sizeof(stage_t));<span style="white-space:pre">				</span>//分配内存空间
		if(new_stage == NULL)
			errno_abort("Allocate stage");
		status = pthread_mutex_init(&new_stage->mutex,NULL);<span style="white-space:pre">		</span>//下面是一些初始化工作
		if(status != 0)
			err_abort(status,"Init stage mutex");
		status = pthread_cond_init(&new_stage->avail,NULL);
		if(status != 0)
			err_abort(status,"Init avail condition");
		status = pthread_cond_init(&new_stage->ready,NULL);
		if(status != 0)
			err_abort(status, "Init ready condition");
		new_stage->data_ready = 0;
		*link = new_stage;
		link = &new_stage->next;
	}
	*link = (stage_t*)NULL;<span style="white-space:pre">	</span>
	pipe->tail = new_stage;
	for(stage = pipe->head;stage->next != NULL;stage = stage->next){<span style="white-space:pre">			</span>//为每个工作单元创建线程
		status = pthread_create(&stage->thread,NULL,pipe_stage,(void*)stage);
		if(status != 0)
			err_abort(status,"Create pipe stage");
	}
	return 0;
}

int pipe_start(pipe_t *pipe, long value){<span style="white-space:pre">					</span>//将新加入的数据发送给工作流处理
	int status;

	status = pthread_mutex_lock(&pipe->mutex);<span style="white-space:pre">			</span>//试图锁定互斥锁以加入新数据
	if(status != 0)
		err_abort(status,"Lock pipe mutex");
	pipe->active++;<span style="white-space:pre">									</span>//如果锁定了互斥锁,说明当前第一个工作单元可以工作,将工作的计数量加一
	status = pthread_mutex_unlock(&pipe->mutex);<span style="white-space:pre">		</span>//解锁互斥锁
	if(status != 0)
		err_abort(status, "Unlock pipe mutex");
	pipe_send(pipe->head,value);<span style="white-space:pre">						</span>//将数据发送给第一个工作单元处理
	return 0;
}

int pipe_result(pipe_t *pipe,long *result){<span style="white-space:pre">					</span>//获取处理结果的函数
	stage_t *tail = pipe->tail;
	long value;<span style="white-space:pre">	</span>//保存结果数据
	int empty = 0;<span style="white-space:pre">		</span>//判断工作流中数据是否为空的flag
	int status;

	status = pthread_mutex_lock(&pipe->mutex);<span style="white-space:pre">			</span>//试图锁定流水线的互斥锁
	if(status != 0)
		err_abort(status, "Lock pipe mutex");
	if(pipe->active <= 0)<span style="white-space:pre">								</span>//如果当前工作中的工作单元小于等于0
		empty = 1;<span style="white-space:pre">									</span>//将empty置为1
	else
		pipe->active--;<span style="white-space:pre">								</span>//否则减少一个工作中的工作单元数(因为取出来来一个数据嘛)
	status = pthread_mutex_unlock(&pipe->mutex);<span style="white-space:pre">		</span>//解锁流水线的互斥锁
	if(status != 0)
		err_abort(status, "Unlock pipe mutex");
	if(empty)<span style="white-space:pre">											</span>//如果流水线中的数据为空,则获取数据失败,函数直接返回0
		return 0;
	
	pthread_mutex_lock(&tail->mutex);<span style="white-space:pre">					</span>//试图锁定最后一个工作单元的互斥锁
	while(!tail->data_ready)<span style="white-space:pre">								</span>//等待最后一个工作单元发送其中存放的数据为可用的有效数矩的信号
		pthread_cond_wait(&tail->avail,&tail->mutex);
	*result = tail->data;<span style="white-space:pre">								</span>//将最后一个工作单元中的有效数据取出存入result中
	tail->data_ready = 0;<span style="white-space:pre">								</span>//最后一个工作单原的数据已取出,则其中的data为过期数据,将data_ready置为0
	pthread_cond_signal(&tail->ready);<span style="white-space:pre">					</span>//并发送信号给其他正在等待中的线程,最后一个工作单元可以处理新的数据了
	pthread_mutex_unlock(&tail->mutex);<span style="white-space:pre">				</span>//锁定最后一个工作单元的互斥锁
	return 1;<span style="white-space:pre">											</span>//获取数据成功,返回1
}

int main(int argc, char *argv[]){<span style="white-space:pre">							</span>//主函数
	pipe_t my_pipe;<span style="white-space:pre">	</span>//定义工作流变量
	long value,result;<span style="white-space:pre">	</span>//value为需要处理的数据,result为处理结果
	int status;
	char line[128];<span style="white-space:pre">	</span>//输入缓存

	pipe_create(&my_pipe, 10);<span style="white-space:pre">							</span>//创建新的工作流(默认创建10个工作单元的工作流)
	printf("Enter integer values, or \"=\" for next result\n");<span style="white-space:pre">	</span>//提示信息

	while(true){<span style="white-space:pre">										</span>//循环等待用户输入并处理数据
		printf("Data>");<span style="white-space:pre">	</span>//提示符
		if(fgets(line,sizeof(line),stdin) == NULL)exit(0);<span style="white-space:pre">	</span>//用户输入结束退出
		if(strlen(line) <= 1) continue;
		if(strlen(line) <= 2 && line[0] == '='){<span style="white-space:pre">				</span>//如果输入 = 表示读取流水线中的处理结果
			if(pipe_result(&my_pipe,&result))<span style="white-space:pre">			</span>//如果读取结果成功
				printf("Result is %ld\n",result);<span style="white-space:pre">			</span>//则将结果打印出来
			else<span style="white-space:pre">										</span>//否则读取失败
				printf("Pipe is empty\n");<span style="white-space:pre">				</span>//打印提示,工作流为空
		}else{
			if(sscanf(line,"%ld",&value) < 1)<span style="white-space:pre">				</span>//将缓存中的数据读出
				fprintf(stderr, "Enter an integer value\n");
			else pipe_start(&my_pipe,value);<span style="white-space:pre">				</span>//调用函数,处理新加入的数据
		}
	}
}



这个例程也是 POSIX多线程程序设计 一书中说明工作流方式的例程, 反复看了很多遍才理清楚其中的逻辑,特别是两个条件变量ready   avail  和  data_ready这个flag的关系.

有几点需要说明的: 

1>因为工作流默认创建10个工作单元,每个工作单元独占一个线程(最后一个工作单元除外,它只负责保存处理结果),所以一共可以同时处理11个数据,如果输入更多数据,整个工作流中的工作单元都在等待下一个工作单元通过ready发出可以接收新数据的信号,而tail所指的工作单原则在等待主线程读出数据,而此时主线程因为加入来新数据而堵塞在等待head所指的工作单元通过ready发出可以接受新数据的信号,从而出现死锁现象,整个进程都被挂起。

2> 每个stage_t中的互斥锁只保护当前工作单元的数据,条件变量也是. 其中ready条件变量是由自己发出给上一个工作单元接收信号的,代表当前工作单原中的数据已经过期,通知上一个工作单原可以将新的数据交给其处理了; avail条件变量是由自己发出给下一个工作单原接收信号的,代表当前工作单元中的数据已经处理完毕,通知下一个数据单元可以拿去继续下一次处理来. 这可能有点绕,但也很好理解:B->C这两个工作单元,如果C中处理完毕的数据还未交给下一个工作单元,也即C中的数据未过期,则B将阻塞,等待C中的数据传送给下一个工作单元处理(此时C中的数据过期),并通过ready发送信号通知B可以将新的数据交给C处理来,这时B将解除阻塞,继续执行.如果C中的数据已经过期,而B中的数据还未处理好(比如没有新的数据加入,或者正在处理新的数据的过程中),则C会阻塞一直等待B通过avail发送信号通知C,B有新的处理好的数据(即有效数据)可以交给C处理来,此时C才会接触阻塞继续工作.    所以ready和avail这两个条件变量是相关联的,最后组合起来就是一个完整的工作流的信号传输体系.

3> 根据第二点就很容易说明第一点的原因来: 因为当前工作流中有11个数据而且从未读出,则最先加入工作流的数据d1在tail所指的工作单元G11中等待读出, 第二加入工作流的数据d2则在第十个工作单原G10中等待G11通过ready发送可以处理新数据的信号,所以G10所在的线程处于阻塞状态.而此时d3所在的G9也同样在等待G10发送ready信号以接收d3进行处理,所以G9所在的线程同样处于阻塞状态,依次类推,d11所在的G1在等待G2的ready信号,线程G1阻塞,因此,此时用户输入的第12个数据d12在主函数中调用pipe_start()欲进行处理时pipe_start()会将数据交给pipe_send(pipe->head,d12)处理,而pipe_send会阻塞在等待G1发出ready的信号的条件变量上,即主线程被阻塞,整个进程被挂起。






本人还在学习中,鉴于水平有限,如果有错误还请指教,谢谢!




### 回答1: 「posix多线程程序设计」是一本介绍多线程编程的参考书籍,主要针对POSIX线程库进行讲解。该书详细介绍了多线程的基本概念、原理以及使用方法。 首先,该书对多线程编程的概念进行了解释,解释了进程和线程的区别,以及为什么使用多线程编程可以提高程序的并发性和响应性。它还解释了多线程并发访问共享数据时可能出现的问题,如竞态条件和死锁,并提供了如何通过加锁和同步机制保证多线程程序的正确性和可靠性。 接下来,该书详细介绍了POSIX线程库的使用方法。它讲解了如何创建线程,如何传递参数给线程函数,以及如何等待线程的结束。此外,它还介绍了线程的属性和状态管理,如设置线程的调度策略和优先级,以及如何取消和终止线程的执行。 此外,该书还介绍了线程间的通信和同步机制。它详细讲解了互斥锁、条件变量、信号量等线程同步的方法,以及它们的使用场景和注意事项。这些内容对于处理复杂的多线程程序非常重要,可以确保多个线程之间的协调和合作。 最后,该书还提供了一些高级的多线程编程技术,如线程池、读写锁、自旋锁等。这些技术可以进一步提高多线程程序的性能和效率。 总之,「posix多线程程序设计」以简洁明了的方式介绍了多线程编程的基本概念、原理和使用方法,并提供了丰富的示例和案例,非常适合有一定编程基础的读者学习和参考。无论是对于Linux系统下的软件开发还是嵌入式系统开发,该书都是一本很好的学习资料。 ### 回答2: POSIX多线程程序设计是一本经典的编程指南,该书通过深入讲解POSIX线程库提供的各种函数和特性,帮助读者掌握多线程编程的基本原理和技巧。本书主要内容包括线程创建和销毁、线程同步与互斥、线程调度、线程间通信等。 首先,该书详细介绍了线程的概念和原理,让读者对多线程编程有一个清晰的认识。然后,通过实例和案例分析,介绍了线程的创建和销毁过程,以及如何设置线程的属性和优先级。 其次,该书重点讲解了线程同步和互斥的技术,包括使用互斥量、条件变量、读写锁等实现线程之间的同步和协作。读者可以了解到如何避免线程冲突、死锁等问题,并学习到如何正确使用同步机制来提高程序的性能和并发度。 此外,该书还介绍了线程调度的相关知识,包括线程的优先级、调度策略和调度器的工作原理,读者可以学习到如何合理设置线程的优先级,以及如何利用调度器来实现多线程程序的高效运行。 最后,该书还介绍了线程间通信的方法,包括使用信号量、消息队列和共享内存等实现线程之间的数据传递和同步。读者可以学习到如何正确使用这些通信机制,以及如何解决多线程并发访问共享资源的问题。 总的来说,POSIX多线程程序设计是一本全面而详细的多线程编程指南,适合有一定编程基础的读者学习。通过学习这本书,读者可以掌握多线程编程的基本原理和技巧,提高程序的并发性和性能,设计出高效稳定的多线程程序。 ### 回答3: 《POSIX多线程程序设计》是一本经典的关于多线程编程的书籍,这本书涵盖了POSIX标准接口中与多线程编程相关的各种知识和技术。这里给出一个简要的300字总结。 该书首先介绍了多线程的概念,并解释了为什么使用多线程可以提高程序的性能和响应性。接着,作者详细介绍了POSIX标准接口中的线程创建、销毁、同步和通信等基本操作,同时也详细解释了这些操作的使用方法和注意事项。 随后,书中介绍了线程的调度和优先级,包括如何设置线程的优先级和如何控制线程的调度顺序。此外,还讨论了线程的并发性和同步机制,包括互斥量、条件变量和信号量等。这些内容帮助读者理解如何实现线程之间的数据共享和协作。 此外,书中还讨论了线程的取消和分离。取消是指在执行过程中终止一个线程的执行,而分离则是指将线程的执行和资源回收分离开来。这些操作在某些特定的场景下非常有用。 最后,书中通过介绍案例和示例程序,帮助读者进一步理解多线程编程的实际应用。通过实践,读者可以更好地掌握多线程编程的技巧和方法。 总体而言,《POSIX多线程程序设计》是一本系统全面介绍了多线程编程的书籍,适合对多线程有一定基础了解的程序员阅读。通过学习该书,读者可以更好地理解和应用多线程编程技术,提高自己的编程水平。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值