Last active
December 27, 2015 13:18
-
-
Save ahappyforest/7331725 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*********************************************************************** | |
* Copyright (C) 2013 by wsn studio *** | |
***********************************************************************/ | |
/** | |
* @file example.h | |
* @author Liu Peng | |
* @date 2013-11-06 | |
* @brief 这是一个小例子, 目的是教你如何快速使用doxygen | |
* | |
* 注意doxygen使用反斜杠@\或者@@符号来定义一个标识 | |
* @see http://www.stack.nl/~dimitri/doxygen/docblocks.html | |
* @see http://www.stack.nl/~dimitri/doxygen/commands.html | |
* @see https://gist.github.com/van9ogh/7331725 | |
*/ | |
#ifndef _EXAMPLE_H | |
#define _EXAMPLE_H | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
/**< 这是一个定义enum的例子*/ | |
/** | |
* @brief enum是个好东西! | |
* | |
* enum里面有三个变量, 我们会拿这三个变量表示物体的三种状态 | |
*/ | |
typedef enum STATUS_enum { | |
STATUS_OPEN, /**< 表示开关已经打开 */ | |
STATUS_CLOSE, /**< 表示已经关闭运行 */ | |
STATUS_RUNNING /**< 表示正在运行 */ | |
} STATUSENUM; | |
/** | |
* @brief 结构体是个好东西 | |
* | |
* 这个结构体用来盛放一些系统运行的变量 | |
*/ | |
typedef struct Example_s { | |
int a; /**< 变量1 */ | |
int b; /**< 变量2 */ | |
double c; /**< 变量3 */ | |
} Example_t; | |
/** | |
* @brief 两个数相加 | |
* | |
* 两个数相加, 我们需要两个整数变量, 然后将它们相加, 例如: | |
* @code{.c} | |
* int c; | |
* c = add(param1, param2); | |
* printf("1 + 1 = %d\n", c); | |
* @endcode | |
* @param a 第一个整数 | |
* @param b 第二个整数 | |
* @return 返回两个数相加后的整数 | |
*/ | |
int add(int a, int b); | |
#endif |
还要吐槽的是queue_get_length其实是不可重入的函数,因为q->length存在竞态,而return并非原子操作,很可能包含内存寻址,加载寄存器ax,pop以及iret等指令,期间只要另外一个线程改写了就欧了。保守的做法是赋值给局部变量,加锁,然后返回该局部变量,或者直接调用atomic_get之类的特殊系统服务函数。
我可能有点理解马力兄你的意思了。
你说的是生产消费者模型, 保证的是读写交替, 因此在读之前要wait, 读完后要signal, 写则是相反的过程。因此会出现读线程会唤醒读线程, 写线程会唤醒写线程的情况。
在queue.c的实现中, 这并不是一个生产消费者模型(至少在时序上不是), 因为写线程一直是可以写的, 没有读线程也可以写。 而且队列在不为空的情况下, 根本就不会wait, 只有当出现两种情况下才会使用pthread_cond_t。
1. queue.c的实现有一个buf pool, 每次put一个新的node都是从pool中取出一个未使用的node, 然后将其插入到单链表中, 于是put操作,当pool为空取不出node的时候, 会发生pthread_cond_wait。
2. 每次get一个新的node都是从单链表中取出一个node, 然后返回node中的key值, 于是get操作, 当单链表为空的时候, 会发生pthread_cond_wait
生产消费者模型的实现, 有使用信号量的(semaphore), 也有使用条件变量的(pthread_cond_t), 个人感觉信号量更合适, 而条件变量本身只是用来触发某种条件的, 它是0,1模型, 在多个读写线程的情况下, 只会把问题搞复杂。
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
pool为空和单链表为空这是具有正交性的互斥命题, 因此我认为读线程并不会唤醒读线程, 写线程不会唤醒写线程。我觉得生产消费者模型, 应该一个信号量就够了。
——是否沉睡/唤醒是pthread_cond_t的内部机制,所以只同信号量有关,只要多个读写线程拥有同一个信号量,那么它们在pthread内部被唤醒的几率是相同的,与外部线程是读是写,以及外部资源是否有效没有关系。至于信号量在互斥锁内这种做法本身有待商榷,我看到的某些版本是解锁后wait唤醒后再上锁。坏情况是,如果读进程只会唤醒另一个读进程,那么它发现没啥可读的从而又进入沉睡,而写线程很有可能长时间得不到唤醒。你可以看看教材中都是用两个信号量的(除非只有一个读线程和一个写线程)。
至于entry和last,设计得相当隐晦啊,连类型都不对称,双向链表的首尾指针,以及环形缓冲的读写下标,都要比这简洁优雅,可见原作者写得相当随意。差评。