之前的一篇,信号量使用错误了,连皮毛都算不上。
这几天看了《深入理解计算机系统》,最后几章有关于信号量同步的。自己也特地演练了一下。
先补充一些感想,在思考为何不断去看书。知识是永远学习不完的,但是思想和认知是可以不断去更新和加深的。
所以,读书并不是为了体现自己的知识面,而是去理解和提高认识!
回到正题,模仿生产者和消费者模型,用队列和信号量进行同步。用信号量来保证入队和出队是“原子”的。
保证访问队列不会因为线程的调度而出现问题。
其实同时对一个变量进行读写,并不会出现什么问题,因为首先系统并不会真的同时做一件事(4核8线程我就不说了,那个是可以)。所以基本不可能我正好在读这个变量时又正好在写这个变量。
翻译成汇编后的c语言,其实是对寄存器的操作。一条c背后有许多汇编,所谓线程调度也是在一条一条汇编的切换。
平常说的线程同步,同步的不是说访问同个变量的系统错误,而是访问同个变量的系统冲突。(可能我用词不严谨,但是我懂……)
贴上代码:
#include <stdio.h> #include <stdlib.h> #include <semaphore.h> typedef struct{ int *buf;//队列数组 int size;//队列大小 int front;//队列前指针 int rear;//队列后指针 sem_t mutex;//用于访问队列的互斥 sem_t slots;//记录空槽信号量 sem_t items;//有效元素信号量 }Queue; Queue *q; //初始化队列 void CreateQueue(Queue *q,int n) { q->buf=(int*)malloc(sizeof(int)*n); if(q->buf==NULL) { printf("Malloc failed "); return; } q->size=n; q->rear=q->front=0; sem_init(&q->mutex,0,1); sem_init(&q->slots,0,n); sem_init(&q->items,0,0); } //释放队列空间 void DeleteQueue(Queue *q) { free(q->buf); sem_destroy(&q->mutex); sem_destroy(&q->slots); sem_destroy(&q->items); } //入队列 void Push(Queue *q,int val) { sem_wait(&q->slots);//空槽信号量-1 //以下3行,访问公有数组 sem_wait(&q->mutex); q->buf[(++q->rear)%(q->size)]=val; sem_post(&q->mutex); sem_post(&q->items);//有效元素信号量+1 } //出队 int Pop(Queue *q) { int data; sem_wait(&q->items);//有效元素信号量-1,如果队列已经空了,则等待 //以下3行,访问公有数组 sem_wait(&q->mutex); data=q->buf[(++q->front)%(q->size)]; sem_post(&q->mutex); sem_post(&q->slots);//空槽信号量+1 return data; } //线程函数,输出每个出队变量,明显这个线程慢于主线程 void *thread_func() { int i=0; while(1) { int data=Pop(q); printf("消费者:"); for(i=data;i<data+10;i++) { printf("%d ",i); } printf(" "); } } int main() { q=(Queue*)malloc(sizeof(Queue)); CreateQueue(q,50);//队列50个数就满 pthread_t a_thread; pthread_create(&a_thread,NULL,thread_func,NULL); int i=0; for(i=0;i<60;i++) { printf("生产者:"); Push(q,i);//60个数入循环队列 printf("%d ",i); } //pthread_join(a_thread,NULL); sleep(2); pthread_cancel(a_thread); DeleteQueue(q); free(q); return 0; }
这是一个不错的代码。不过,当生产者已经生产结束了。如何高速消费者,已经结束了。
此时,消费者应该是卡在了,这一句:
sem_wait(&q->items);//有效元素信号量-1,如果队列已经空了,则等待
因为队列已经空了。所以不能再消费者线程里面加什么全局变量或者其他的东西来停止消费者(我默认消费者要不停的工作,而不是已知它的工作次数,所以放在while循环里,正常的消费者工作也应该是这样,只知道开始和结束)。
鉴于这个原因,只能在主线程里面停止消费者了。但是什么时候知道消费者已经处理完了呢?我这里偷个懒,sleep(2),这可以保证做完,但这真是个破方法。
如果需要真正的主线程发信号,还得在POP中修改。比如再加一个信号量。主线程发n次信号,子线程就n次。这也可以,不过我就不实现了。