[多线程]使用信号量进行同步

  信号量是最早出现的用来解决进程同步与互斥问题的机制(也可实现进程通信),包括一个称为信号量的变量及对它进行的两个原语操作。信号量为一个整数,我们设这个信号量为:sem。很显然,我们规定在sem大于等于零的时候代表可供并发进程使用的资源实体数,sem小于零的时候,表示正在等待使用临界区的进程的个数。根据这个原则,在给信号量附初值的时候,我们显然就要设初值大于零。

p操作和v操作是不可中断的程序段,称为原语。P,V原语中P是荷兰语的Passeren,相当于英文的pass, V是荷兰语的Verhoog,相当于英文中的incremnet。

且在P,V愿语执行期间不允许有中断的发生。

首先应弄清PV操作的含义:PV操作由P操作原语和V操作原语组成(原语是不可中断的过程),对信号量进行操作,具体定义如下:

P(S):①将信号量S的值减1,即S=S-1;②如果S>=0,则该进程继续执行;否则该进程置为等待状态,排入等待队列。

V(S):①将信号量S的值加1,即S=S+1;②如果S>0,则该进程继续执行;否则释放队列中第一个等待信号量的进程。

PV操作的意义:我们用信号量及PV操作来实现进程的同步和互斥。PV操作属于进程的低级通信。

什么是信号量?信号量(semaphore)的数据结构为一个值和一个指针,指针指向等待该 信号量的下一个进程。信号量的值与相应资源的使用情况有关。当它的值大于0时,表示当前可用资源的数量;当它的值小于0时,其绝对值表示等待使用该资源的进程个数。注意,信号量的值仅能由PV操作来改变。

一般来说,信号量S>=0时,S表示可用资源的数量。执行一次P操作意味着请求分配一个单位资源,因此S的值减1;

当S<0时,表示已经没有可用资源,请求者必须等待别的进程释放该类资源,它才能运行下去。而执行一个V操作意味着释放一个单位资源,因此S的值加1;

若S<=0,表示有某些进程正在等待该资源,因此要唤醒一个等待状态的进程,使之运行下去。

对信号量有4种操作(include<semaphore>):
1. 初始化(initialize),int set_init(sem_t *sem, int pshared, unsigned int value);//第二参数为0表示进程间不共享
2. 等信号(wait),int sem_wait(sem_t *sem);//信号量大于1时,减一并返回;小于1时线程阻塞。
3. 给信号(signal)int sem_post(sem_t *sem);//信号量加一
4. 清理(destory) int sem_destory(sem_t *sem);

使用信号量实现生产者-消费者例子:

#define BUFFER_SIZE 16 // 缓冲区数量

struct prodcons
{
    // 缓冲区相关数据结构
    int buffer[BUFFER_SIZE]; /* 实际数据存放的数组*/
    int readpos, writepos; /* 读写指针*/
    sem_t empty; /* 缓冲区非空的条件变量 */
    sem_t occupied; /* 缓冲区未满的条件变量 */
    sem_t s_put;/*输入互斥信号量*/
    sem_t s_take;/*取出互斥信号量*/
};
/* 初始化缓冲区结构 */
void init(struct prodcons *b)
{
    b->readpos = 0;
    b->writepos = 0;
    sem_init(&b->empty,0,BUFFER_SIZE);
    sem_init(&b->occupied,0,0);
    sem_init(&b->s_put,0,1);//二进制信号量,相当于互斥锁
    sem_init(&b->s_take,0,1);
}
/* 将产品放入缓冲区,这里是存入一个整数*/
void put(struct prodcons *b, int data)
{
    /*查看空位信号量,是否可以放入产品*/
    sem_wait(&b->empty);
    /*查看是否有其他线程正在放入,同一时刻只能一个线程放入*/
    sem_wait(&b->s_put);
    /* 写数据,并移动指针 */
    b->buffer[b->writepos] = data;
    b->writepos++;
    if (b->writepos >= BUFFER_SIZE)
        b->writepos = 0;
    sem_post(&b->s_put);//解除互斥
    sem_post(&b->occupied);//放入完成,被占位置信号量加一
    
} 
/* 从缓冲区中取出整数*/
int get(struct prodcons *b)
{
    int data;
    /*查看被占信号量,是否有产品可以拿出*/
    sem_wait(&b->occupied);
    /*查看是否有其他线程正在拿出,同一时刻只能一个线程拿出*/
    sem_wait(&b->s_take);
    /* 读数据,移动读指针*/
    data = b->buffer[b->readpos];
    b->readpos++;
    if (b->readpos >= BUFFER_SIZE)
        b->readpos = 0;
    sem_post(&b->s_take);//解除互斥
    sem_post(&b->empty);//拿出完成,被空位信号量加一
    return data;
}

/* 测试:生产者线程将1 到100 的整数送入缓冲区,消费者线
   程从缓冲区中获取整数,两者都打印信息*/
#define OVER ( - 1)
struct prodcons buffer;
void *producer(void *data)
{
    int n;
    for (n = 0; n < 100; n++)
    {
        printf("%d --->\n", n);
        put(&buffer, n);
    } put(&buffer, OVER);
    return NULL;
}

void *consumer(void *data)
{
    int d;
    while (1)
    {
        d = get(&buffer);
        if (d == OVER)
            break;
        printf("--->%d \n", d);
    }
    return NULL;
}

int main(void)
{
    pthread_t th_a, th_b;
    void *retval;
    init(&buffer);
    /* 创建生产者和消费者线程*/
    pthread_create(&th_a, NULL, producer, 0);
    pthread_create(&th_b, NULL, consumer, 0);
    /* 等待两个线程结束*/
    pthread_join(th_a, &retval);
    pthread_join(th_b, &retval);
    return 0;
}

 

posted @ 2013-06-19 18:53  iyjhabc  阅读(2228)  评论(0编辑  收藏  举报