进程同步与互斥:LINUX环境
⼀、实验⽬的:
1. 掌握基本的同步与互斥算法,理解P,V操作。
2. 理解⽣产者消费者模型,了解其它典型的同步互斥模型,如哲学家就餐、读者-写者模型等。
3. 了解LINUX中进程同步互斥的实现⽅法,掌握相关函数的使⽤⽅法。
4. 学习使⽤Windows中基本的同步对象,掌握相关API的使⽤⽅法。
5. 了解Windows中多线程的并发执⾏机制,实现进程的同步与互斥。
⼆、实验环境:
LINUX环境
三、实验内容:
PART 1 LINUX环境
⼀、相关知识介绍
1.常⽤线程函数
pthread线程库的使⽤,在源码中使⽤头⽂件 pthread.h,⽤gcc链接时加上 -lpthread 选项,链接线程库。
(1) pthread_create 创建⼀个线程
int pthread_create(pthread_t *thread, const pthread_attr_t attr,void(start_rtn)(void),void * arg);
 参数1:产⽣线程的标识符
 参数2:所产⽣线程的属性,通常设为NULL
参数3:新的线程所执⾏的函数
参数4:新的线程函数的参数
函数返回值:若线程创建成功则返回0,否则返 回-1.
(2) pthread_exit 线程⾃⾏退出
(3) pthread_join 其它线程等待某⼀个线程退出
int pthread_join( pthread_t thread , void **value_ptr);
 参数1:等待线程的标识符
 参数2:⽤户定义的指针,⽤来存储被等待线程的返回值
函数返回值:若执⾏成功则返回0,失败返回错误号, 如果⼀个线程要等待另⼀个线程的终⽌,则使⽤该函数
(4) pthread_cancel 其它线程强⾏杀死某⼀个线程
2. 互斥锁使⽤⽅法
(1) 互斥锁操作相关函数
pthread_mutex_init: 互斥量初始化
pthread_mutex_lock: 互斥量加锁
pthread_mutex_unlock: 释放互斥锁
pthread_mutex_destroy: 释放互斥锁资源
(2)使⽤互斥锁对临界区进⾏操作的⽅法:
使⽤函数pthread_mutex_lock进⾏加锁
临界区操作
使⽤函数pthread_mutex_unlock释放互斥锁
3. 信号量使⽤⽅法
(1) 信号量相关函数
信号量的数据类型为sem_t
#include<semaphore.h>
相关函数: sem_init 、sem_wait、 sem_post、 sem_destroy
int sem_init (sem_t *sem, int pshared, unsigned int value);
参数1: 待初始化的信号量
参数2: 信号量的类型。如果值为0,表⽰是当前进程的局部信号量,否则是其它进程可以共享该信号量。 LINUX线程⼀般不⽀持进程间共享信号量,该值设为0.
参数3:信号量的初始值。
调⽤成功时返回0,失败返回-1.
int sem_wait(sem_t * sem);
参数: 由sem_init调⽤初始化的信号量对象的指针 sem的值减1等待信号量,如果信号量的值⼤于0,将信号量的值减1,⽴即返回。如果信号量的值为0,则线程阻塞。相当于P操作。
成功返回0,失败返回-1
int sem_post(sem_t * sem);
参数: 由sem_init调⽤初始化的信号量对象的指针
释放信号量,让信号量的值加1。相当于V操作。
int sem_destroy(sem_t * sem);
参数: 由sem_init调⽤初始化的信号量对象的指针
归还⾃⼰占据的⼀切资源。 在清理信号量的时候如果还有线程在等待它,⽤户就会收到⼀个错误。
(2) 信号量使⽤⽅法
声明⼀个信号量sem_t my_sem;
初始化信号量sem_init(& my_sem,0,1);
sem_post和sem_wait函数配合使⽤来达到线程同步
释放信号量int sem_destroy (& my_sem);
⼆、实验内容
1. ⽣产者-消费者问题
(1)以下给出⽣产者-消费者问题的基本框架,未实现同步互斥控制,仔细查看程序运⾏结果,分析原因。
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include <time.h>
#define N 4 // 消费者或者⽣产者的数⽬
#define M 20 // 缓冲数⽬
int in = 0; // ⽣产者放置产品的位置
int out = 0; // 消费者取产品的位置
char buff[M]; // 缓冲区
int producter_id = 0; //⽣产者id
int consumer_id = 0; //消费者id
/* 打印缓冲情况 */
void print()
{
int i;
for(i = 0; i < M; i++)
printf("%c ", buff[i]);
printf("\n");
}
/* ⽣产者⽅法 */
void *producter()
{
int id = ++producter_id;
while(1)
{
// ⽤sleep的数量可以调节⽣产和消费的速度
sleep(2);
char data;
data=rand()%26+65;
in = in % M;
printf("⽣产者进程%d在%2d位置产⽣数据%c: ", id,in,data);
buff[in] = data;
print();
++in;
}
}
/* 消费者⽅法 */
void *consumer()
void *consumer()
{
char data;
int id = ++consumer_id;
while(1)
{
// ⽤sleep的数量可以调节⽣产和消费的速度
sleep(1);
out = out % M;
data=buff[out];
printf("消费者进程%d在%2d位置消费数据%c: ",id, out,data); buff[out] = '*';
print();
++out;
}
}
int main()
{
pthread_t p[N];
pthread_t c[N];
int i;
int ret[N];
for(i=0; i<M; i++)
buff[i]='*'; //'*'表⽰空,初始化缓冲区
srand((int)time(NULL));
// 创建N个⽣产者线程
for (i = 0; i < N; i++)
{
ret[i] = pthread_create(&p[i], NULL,(void*)producter, (void *)(&i)); if(ret[i] != 0)
{
printf("producter %d creation failed \n", i);
exit(1);
}
}
//创建N个消费者线程
for(i = 0; i < N; i++)
{
ret[i] = pthread_create(&c[i], NULL, (void*)consumer, NULL);
if(ret[i] != 0)
{
printf("consumer %d creation failed\n", i);
exit(1);
}
}
//销毁线程
for(i = 0; i < N; i++)
{
pthread_join(p[i],NULL);
pthread_join(c[i],NULL);
}
exit(0);
}
```bash
1. 关系分析。⽣产者和消费者对缓冲区互斥访问是互斥关系,同时⽣产者和消费者⼜是⼀个相互协作的关系,只有⽣产者⽣产之后,消
费者才能消费,他们也是同步关系。
2. 整理思路。这⾥⽐较简单,只有⽣产者和消费者两个进程,正好是这两个进程存在着互斥关系和同步关系。那么需要解决的是互斥和
同步PV操作的位置。
3. 信号量设置。信号量mutex作为互斥信号量,它⽤于控制互斥访问缓冲池,互斥信号量初值为1;信号量full⽤于记录当前缓冲池
中“满”缓冲区数,初值为0。信号量empty ⽤于记录当前缓冲池中“空”缓冲区数,初值为n。
(2) 在代码中加⼊同步互斥控制功能,使程序能够实现⽣产者-消费者问题。
实现⽅法参考教材109页算法,提⽰:
使⽤两个同步信号量,sem_t empty_sem; // 同步信号量, 当满了时阻⽌⽣产者放产品
sem_t full_sem; // 同步信号量, 当没产品时阻⽌消费者消费
使⽤⼀个互斥信号量,pthread_mutex_t mutex; // 互斥信号量, ⼀次只有⼀个线程访问缓冲
#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 4  //消费者或者⽣产者的数⽬
#define M 20  //缓冲数⽬
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)
while(1)
{
Sleep(5000); //毫秒
sem_wait(&empty_sem); //给信号量减1操作
linux下的sleep函数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) );

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。