看了一些关于信号量的线程同步方式,今天用了一下。
我对于线程同步一直有疑问,在主线程和子线程处理时间不相同的时候,用这种信号量,如何保证同步。
假如主线程比较快,信号量连加了n个,但是子线程就不断减这个n,减到0。但是如果主线程太快太快,需要停一停,比如缓冲区快溢出了,主线程需要挂起。
由什么来唤醒主线程呢?子线程?不过这样的话,容易造成主线程死锁,或者主和子都卡死。
下面的程序,没有用到信号量同步,信号量只是负责开启子线程而已。主要是队列的实现而已。等我把上面的问题解决完会写上更新的程序。
队列头文件:
#ifndef _queue_H #define _queue_H typedef int T; typedef struct queue Queue; struct queue{ T data; Queue* next; }; static int length=0; Queue* CreateQueue(); void DestroyQueue(Queue* h); int Push(Queue* h,T t); Queue* Pop(Queue* h); void Print(Queue* h); #endif
队列c文件:
#include <stdio.h> #include <stdlib.h> #include "queue.h" const int MaxSize=50; Queue* CreateQueue() { Queue* h=(Queue*)malloc(sizeof(Queue)); if(h==NULL) { printf("Malloc failed!\n"); return NULL; } h->next=NULL; return h; } void DestroyQueue(Queue* h) { Queue* p=h; while(p!=NULL) { Queue* tmp=p; p=p->next; free(tmp); } } int Push(Queue* h,T t) { Queue* tmp=(Queue*)malloc(sizeof(Queue)); if(tmp==NULL) { printf("Malloc failed!\n"); return; } tmp->data=t; tmp->next=NULL; if(length>=MaxSize) { printf("Queue is full!\n"); return -1; } Queue* p=h; while(p->next!=NULL) { p=p->next; } p->next=tmp; length++; } Queue* Pop(Queue* h) { Queue* res=h->next; if(h->next==NULL) { //printf("Queue is empty!\n"); return NULL; } h->next=h->next->next; length--; return res; } void Print(Queue* h) { Queue* p=h->next; while(p!=NULL) { printf("%d\n",p->data); p=p->next; } printf("Above is the Queue!\n"); printf("\n"); }
主程序:
#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <semaphore.h> #include "queue.h" /* 此程序模仿生产者-消费者模型 生产者:0-99的int型数,进入队列。如果队列(默认最大50)满了,提示已满,主线程等待直到队列可以进入。 消费者:对生产者产生的数据计算并输出10个结果。如果队列空了,提示已空。 通过队列和信号量来同步。 此程序以主线程为主,因为相互发信号会导致死锁,程序卡住!应该是技术不够,不过也至于冒这个险! */ sem_t btn_sem;//二进制信号量 sem_t flag_sem; Queue *head;//队列头部 int End=0;//主线程结束标志位 //子线程执行函数 void *thread_func(void *arg) { int i=0; sem_wait(&btn_sem);//子线程等待开始 while(1) { Queue *tmp=Pop(head); if(tmp==NULL)//队列已经空了 { } else { printf("Chlid Thread! "); for(i=tmp->data;i<tmp->data+10;i++) { printf("%d ",i); } printf("\n");//子线程输出 free(tmp); } if(End==1) { if(head->next==NULL) return; } } } int main() { head=CreateQueue(); int i=0; char aa[]="aaaa!"; pthread_t a_thread; int res; res=sem_init(&btn_sem,0,0); if(res!=0) { printf("Signal initials failed!\n"); return; } res=sem_init(&flag_sem,0,0); if(res!=0) { printf("Signal initials failed!\n"); return; } res=pthread_create(&a_thread,NULL,thread_func,(void*)aa); if(res!=0) { printf("Create thread failed!\n"); return; } sem_post(&btn_sem);//第一次进入主循环时发信号,告知子线程可以开始 //主线程,生产者 for(i=0;i<100;i++) { res=Push(head,i);//数据直接进入队列 if(res==-1)//如果队列满了,需要等待 { while(Push(head,i)==-1);//放到队列有空间为止 //Push(head,i);//子线程发信号,表示队列有空间了,此时把等待的信息push进队列 } printf("%d\n",i); } End=1; void *thread_res; pthread_join(a_thread,&thread_res);//等待子线程结束后,结束主线程 sem_destroy(&btn_sem); sem_destroy(&flag_sem); DestroyQueue(head); return 0; }
makefile:
main:main.o queue.o gcc -o main main.o queue.o -pthread main.o:main.c queue.h gcc -c main.c -pthread queue.o:queue.c queue.h gcc -c queue.c
如果队列满了,这里不采用等待子线程做,而是不断访问队列,等待队列有空间,其实也就是等待子线程操作。
不过这种方式不好!
因为主线程其实还是一直在做自己的事情,CPU并不知道此时需要少分时间片给主线程,而是按照正常的分配给主和子。
理想情况,此时应该主线程挂起,主要给子线程,让其运行,产生多余的队列空间。
所以以上程序的效率是不高的。
时间: 2024-12-09 18:52:36