1. 程式人生 > >【Linux】POSIX訊號量

【Linux】POSIX訊號量

POSIX訊號量和SystemV訊號量作⽤相同,都是⽤於同步操作,達到⽆衝突的訪問共享資源⺫的。 但POSIX可以⽤於執行緒間同步。

一個計數器+等待佇列,計數器用來標記當前是否有資源可供操作,等待佇列則是沒有資源就將pcb加入佇列中,等有資源的時候喚醒佇列中的執行緒。

訊號量的操作就是對計數器的+1/-1操作,當計數器<=0則表示沒有資源,這時候陷入等待;如果別人釋放了資源,喚醒等待。

訊號量用於實現程序/執行緒之間的同步與互斥。

初始化訊號量

int sem_init(sem_t *sem, int pshared, unsigned int value);
引數:
 pshared:0表⽰執行緒間共享,⾮零表⽰程序間共享
 value:訊號量初始值
返回值:成功0;失敗:-1

銷燬訊號量

int sem_destroy(sem_t *sem);

獲取訊號量(等待訊號量)

功能:等待訊號量,會將訊號量的值減1
int sem_wait(sem_t *sem);判斷計數器是否大於零,大於零表示有資源可操作,這時候計數器-1,如果小於等於0代表沒有資源可操作,這時候則陷入等待
int sem_trywait();
int sem_timewait();

釋出訊號量

功能:釋出訊號量,表⽰資源使⽤完畢,可以歸還資源了。將訊號量值加1。
int sem_post(sem_t *sem);

實現執行緒同步

#include <iostream>
#include<unistd.h>
#include<stdlib.h>
#include<semaphore.h>
#include<errno.h>
#include<pthread.h>
using namespace std;

sem_t sem;
void* productor(void* arg)
{
    while(1)
    {
        sleep(1);
        cout << "create noodle!!!" << endl;
        sem_post(&sem);
    }
    return NULL;
}

void* consumer(void* arg)
{
    while(1)
    {
        sem_wait(&sem);
        cout << "eat noodle!!!" << endl;
    }
    return NULL;
}
int main()
{
    pthread_t tid1,tid2;
    
    sem_init(&sem, 0, 0);

    pthread_create(&tid1, NULL, productor, NULL);
    pthread_create(&tid2, NULL, consumer, NULL);

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    
    sem_destroy(&sem);
    return 0;
}

實現執行緒互斥

#include <iostream>
#include<unistd.h>
#include<stdlib.h>
#include<semaphore.h>
#include<errno.h>
#include<pthread.h>
using namespace std;

sem_t sem;
void* productor(void* arg)
{
    while(1)
    {
        sem_wait(&sem);
        cout << "create noodle!!!" << endl;
        sem_post(&sem);
        usleep(10);
    }
    return NULL;
}

void* consumer(void* arg)
{
    while(1)
    {
        sem_wait(&sem);
        cout << "eat noodle!!!" << endl;
        sem_post(&sem);
        usleep(10);
    }
    return NULL;
}
int main()
{
    pthread_t tid1,tid2;
    
    sem_init(&sem, 0, 1);

    pthread_create(&tid1, NULL, productor, NULL);
    pthread_create(&tid2, NULL, consumer, NULL);

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    
    sem_destroy(&sem);
    return 0;
}

用訊號量實現環形佇列

/*用vector實現環形佇列
 * 使用訊號量來對這個環形佇列資源進行計數
 * 兩個訊號量:
 *  一個是空閒空間資源計數       等於vector節點數
 *  一個是寫入資料的空間計數     初始值為0
*/
#include <iostream>
#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>
#include<semaphore.h>
#include<vector>
using namespace std;
#define MAX_QUEUE 10
class RingQueue
{
private:
    vector<int> _q;
    int _cap;
    int _consumer_step;
    int _productor_step;
    sem_t _idle;
    sem_t _data;
public:
    RingQueue(int cap = MAX_QUEUE):_cap(cap),_q(cap)
    {
        _consumer_step = 0;
        _productor_step = 0;
        sem_init(&_idle, 0, cap);
        sem_init(&_data, 0, 0);
    }

    ~RingQueue()
    {
        sem_destroy(&_data);
        sem_destroy(&_idle);
    }

    bool GetData(int& data)
    {
        sem_wait(&_data);
        data = _q[_consumer_step++];
        _consumer_step %= _cap;
        sem_post(&_idle);
        return true;
    }

    bool PutData(int data)
    {
        sem_wait(&_idle);
        _q[_productor_step++] = data;
        _productor_step %= _cap;
        sem_post(&_data);
        return true;
    }
};

void* consumer(void* arg)
{
    RingQueue *q = (RingQueue*)arg;
    while(1)
    {
        int data;
        q->GetData(data);
        cout << "data:" << data << endl;
    }
    return NULL;
}

void* productor(void* arg)
{
    RingQueue *q = (RingQueue*)arg;
    int i = 0;
    while(1)
    {
        q->PutData(i++);
        cout << "put data:" << i << endl;
    }
    return NULL;
}

int main()
{
    RingQueue q;
    pthread_t tid1, tid2;
    pthread_create(&tid1, NULL, consumer, (void*)&q);
    pthread_create(&tid2, NULL, productor, (void*)&q);

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    return 0;
}