信号量 (Semaphore)是表示资源的实体,是一个与队列有关的整形变量,其值仅能由P、V操作来改变。公用信号量常用于实现进程间互斥,初始值通常设为1。私用信号量常用于用于实现进程间同步,初始值通常为0或n
当一个进程想要访问临界区资源时,它必须先执行P原语(系统态下执行的某些具有特定功能的程序段)操作将信号量sem减1。若sem>=0,则可以访问资源,否则进程挂起。当它完成对临界区资源操作后,执行V原语操作将sem加1,以释放资源。
sem初试值为1,所以任意进程执行完P操作后sem值变为0,该进程进入临界区。在此进程执行V操作之前,另一进程想要访问临界区,执行P操作后sem值变为-1,因此该进程阻塞。第一个进程执行V操作后,sem值变为0,从而第二个进程唤醒进入就绪队列。第二个进程执行V操作后,如果没有其它进程申请进入临界区,sem值恢复到初始值1。
生产者-消费者问题是典型的PV操作问题。生产者的任务是生成一定量的数据放到缓冲区,并且重复此过程。消费者的任务会从缓冲区拿走数据,并且重复此过程。关键在于保证生产者不会在缓冲区满时继续生产,消费者不会在缓冲区为空时继续消费。缓冲区满时,生产者进程需要阻塞。缓冲区为空时,消费者进程需要阻塞。并且消费者生产这不能同时访问缓冲区。
信号量empy判断生产者是否还可以生产,信号量full判断是否还可以消费,信号量mutex用于互斥访问缓冲区。empty、full是私用信号量,用于进程同步,mutex是公用信号量,用于进程互斥。
//生产者进程
producer:
生产产品;
p(empty); //空的时候信号量empty为缓冲区大小
p(mutex);
送产品进入缓冲区
v(mutex);
v(full); //满的时候信号量0
//生产者进程
consumer:
p(full);
p(mutex);
从缓冲区取走产品
v(mutex);
v(empty);
消费产品;
创建PRODUCE个线程模拟生产者,CONSUMER个线程模拟消费者,一个数组模拟缓冲区情况。
#define PRODUCER 4 //生产者数量
#define CONSUMER 4 //消费者数量
#define BUFFER 8 //缓冲区大小
int producer_id = 0;
int consumer_id = 0;
int in = 0;
int out = 0;
int Buffer[BUFFER];
sem_t sem_empty; //同步信号量
sem_t sem_full;
pthread_mutex_t mutex; //互斥信号量
void Signal_print(); //处理信号
void print(); //打印缓冲队列
void *producer(); //生产者
void *consumer(); //消费者
void *producer(){
int id = ++producer_id;
while(1){
sleep(3); //调节速度
sem_wait(&sem_empty); //p(empty)操作
pthread_mutex_lock(&mutex); //p(mutex)操作
in %= BUFFER;
printf("生产者%d将产品放入缓冲区队列第%d号 ",id,in+1);
Buffer[in] = 1;
print();
++in;
pthread_mutex_unlock(&mutex); //v(mutex)操作
sem_post(&sem_full); //v(full)操作
}
}
void *consumer(){
int id = ++consumer_id;
while(1){
sleep(3);
sem_wait(&sem_full);
pthread_mutex_lock(&mutex);
out %= BUFFER;
printf("消费者%d从缓冲区队列第%d号取走产品 ",id,out+1);
Buffer[out] = 0;
print();
++out;
pthread_mutex_unlock(&mutex);
sem_post(&sem_empty);
}
}
关于其中用到的函数。在Linux下可以man查看函数的说明,pthread需要单独添加apt安装glibc-doc manpages-posix-dev
创建一个子进程。
#include
#include
pid_t fork(void);
返回值:
On success, the PID of the child process is returned in the parent, and 0
is returned in the child. On failure, -1 is returned in the parent, no
child process is created, and errno is set appropriately.
初始化信号量。
#include
int sem_init(sem_t *sem, int pshared, unsigned int value);
Link with -pthread.
sem_init() initializes the unnamed semaphore at the address pointed to by
sem. The value argument specifies the initial value for the semaphore.
The pshared argument indicates whether this semaphore is to be shared
between the threads of a process, or between processes.
If pshared has the value 0, then the semaphore is shared between the
threads of a process, and should be located at some address that is visible
to all threads (e.g., a global variable, or a variable allocated dynami‐
cally on the heap).
If pshared is nonzero, then the semaphore is shared between processes, and
should be located in a region of shared memory (see shm_open(3), mmap(2),
and shmget(2)). (Since a child created by fork(2) inherits its parent’s
memory mappings, it can also access the semaphore.) Any process that can
access the shared memory region can operate on the semaphore using
sem_post(3), sem_wait(3), and so on.
Initializing a semaphore that has already been initialized results in unde‐
fined behavior
返回值:
sem_init() returns 0 on success; on error, -1 is returned, and errno is set
to indicate the error.
创建线程。
//Compile and link with -pthread.
#include
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
返回值:
On success, pthread_create() returns 0; on error, it returns an error num‐
ber, and the contents of *thread are undefined.
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t
*mutexattr);
附:开篇处下载链接需要3金币,如果实在需要又没有可以给我发邮件免费获取。A mutex has two possible states: unlocked (not owned by any thread), and
locked (owned by one thread). A mutex can never be owned by two different
threads simultaneously. A thread attempting to lock a mutex that is already
locked by another thread is suspended until the owning thread unlocks the
mutex first
pthread_mutex_init initializes the mutex object pointed to by mutex accord‐
ing to the mutex attributes specified in mutexattr. If mutexattr is NULL,
default attributes are used instead.
pthread_mutex_init always returns 0. The other mutex functions return 0 on
success and a non-zero error code on error.