一、什么是信号量
信号量是一种用于同步多线程或进程之间的机制,用于控制对共享资源的访问。信号量可以用来保护临界区,防止多个线程同时访问某个共享资源导致数据不一致或竞争条件的发生。
二、什么是进程的同步?
进程同步就是指协调完成某个共同任务的并发线程,在某些位置上指定线程的先后执行次序、传递信号或消息
三、什么是进程的互斥?
在一个时间段内只允许一个进程使用的资源,我们将其称为临界资源,对临界资源进行访问的那段代码称为临界区。
进程互斥是一种特殊的进程同步,即逐次使用临界资源,也是对进程使用资源的先后执行次序的一种协调。
四、信号量与PV操作
用户进程可以通过使用操作系统提供的一对原语来对信号量进行操作,从而很方便的实现进程互斥或同步。这一对原语就是 PV 操作:
- P 操作:
将信号量值减 1,表示申请占用一个资源。如果结果小于 0,表示已经没有可用资源,则执行 P 操作的进程被阻塞。如果结果大于等于 0,表示现有的资源足够你使用,则执行 P 操作的进程继续执行。
代码示例:
P(semaphore S) {
while (S <= 0) {
// 阻塞,进入等待队列
}
S = S - 1; // 信号量减1
}
- V 操作:
将信号量值加 1,表示释放一个资源,即使用完资源后归还资源。若加完后信号量的值小于等于 0,表示有某些进程正在等待该资源,由于我们已经释放出一个资源了,因此需要唤醒一个等待使用该资源(就绪态)的进程,使之运行下去。
代码示例:
V(semaphore S) {
S = S + 1; // 信号量加1
if (有等待进程) {
唤醒一个等待进程;
}
}
五、信号量的代码实现
1. 初始化
static int semid = -1;
void sem_init()
{
semid = semget((key_t)1234,1,IPC_CREAT|IPC_EXCL|0600);//全新创建一个信号量
if(semid==-1)//如果失败,说明已经创建,直接获取id即可
{
semid=semget((key_t)1234,1,0600);//获取别人创建的信号量
if(semid==-1)
{
printf("semget err\n");
return;
}
}
else
{
union semun a;
a.val=1;
if( semctl(semid,0,SETVAL,a) == -1)
{
printf("semctl init err\n");
}
}
}
2.反初始化
void sem_uninit()
{
if(semctl(semid,0,IPC_RMID) == -1)
{
printf("semctl uninit error\n");
}
}
3. p操作
void sem_p()
{
struct sembuf buf;
buf.sem_num = 0;
buf.sem_op = -1;//p
buf.sem_flg = SEM_UNDO;
if(semop(semid,&buf,1) ==-1)
{
printf("p err\n");
}
}
4. V操作
void sem_v()
{
struct sembuf buf;
buf.sem_num = 0;
buf.sem_op = 1;//v
buf.sem_flg = SEM_UNDO;
if(semop(semid,&buf,1) ==-1)
{
printf("v err\n");
}
}
六、生产者和消费者问题
#include "stdafx.h"
#include <stdio.h>
#include <pthread.h>
#include <sched.h>
#include <semaphore.h>
#include <conio.h>
#include <ctype.h>
#include <signal.h>
#include <iostream>
#include<Windows.h>
using namespace std;
#pragma comment(lib,"pthreadVC2.lib")
#define N 5 //消费者或者生产者的数目
#define M 10 //缓冲数目
int productin = 0; //生产者放置产品的位置
int prochaseout = 0; //消费者取产品的位置
int buff[M] = {0}; //缓冲区初始化为0,开始时没有产品。
sem_t empty_sem; // 同步信号量,当满的时候阻止生产者放产品。
sem_t full_sem; //同步信号量,当没有产品的时候阻止消费者消费。
pthread_mutex_t mutex; //互斥信号量,一次只有一个线程访问缓冲区。
int product_id = 0; //生产者id
int prochase_id = 0; //消费者id
void SignalExit(int signo)
{
printf("程序退出%d\n",signo);
return;
}
void PrintProduction()
{
printf("此时的产品队列为::");
for(int i = 0; i < M; i++ )
{
printf("%d ",buff[i]);
}
printf("\n\n");
}
//生产者方法
void* Product(void* pramter)
{
int id = ++product_id;
while(1)
{
Sleep(5000); //毫秒
sem_wait(&empty_sem); //给信号量减1操作
pthread_mutex_lock(&mutex);
productin = productin % M;
printf("生产者%d在产品队列中放入第%d个产品\n\n",id,productin+1);
buff[productin] = 1;
PrintProduction();
++productin;
pthread_mutex_unlock(&mutex); //释放互斥量对象
sem_post(&full_sem); //给信号量的值加1操作
}
}
//消费者方法///
void* Prochase( void* pramter )
{
int id = ++prochase_id;
while(1)
{
Sleep(7000);
sem_wait(&full_sem);
pthread_mutex_lock(&mutex);
prochaseout = prochaseout % M;
printf("消费者%d从产品队列中取出第%d个产品\n\n",id,prochaseout+1);
buff[prochaseout] = 0;
PrintProduction();
++prochaseout;
pthread_mutex_unlock(&mutex);
sem_post(&empty_sem);
}
}
int main()
{
cout << "生产者和消费者数目都为5,产品缓冲区为10,生产者每2秒生产一个产品,消费者每5秒消费一个产品" << endl << endl;
pthread_t productid[N];
pthread_t prochaseid[N];
int ret[N];
//初始化信号量
int seminit1 = sem_init(&empty_sem,0,M);
int seminit2 = sem_init(&full_sem,0,0);
if( seminit1 != 0 && seminit2 != 0 )
{
printf("sem_init failed !!!\n");
return 0;
}
//初始化互斥信号量
int mutexinit = pthread_mutex_init(&mutex,NULL);
if( mutexinit != 0 )
{
printf("pthread_mutex_init failed !!\n");
return 0;
}
//创建n个生产者线程
for(int i = 0; i < N; i++ )
{
ret[i] = pthread_create( &productid[i], NULL,Product,(void*)(&i) );
if( ret[i] != 0 )
{
printf("生产者%d线程创建失败!\n",i);
return 0;
}
}
//创建n个消费者线程
for(int j = 0; j < N; j++ )
{
ret[j] = pthread_create(&prochaseid[j],NULL,Prochase,NULL);
if( ret[j] != 0 )
{
printf("消费者%d线程创建失败\n",j);
return 0;
}
}
///等待线程被销毁///
for( int k = 0; k < N; k++ )
{
printf("销毁线程\n");
pthread_join(productid[k],NULL);
pthread_join(prochaseid[k],NULL);
}
return 0;
}