完整源码见本人博客下载资源
文章目录
一、完成情况概览
- 熟悉了解了nachos同步机制,信号量,锁,条件变量与管程。
- 用信号量解决生产者/消费者同步问题
- 用管程解决生产者/消费者同步问题
二、Nachos的同步机制研究
以下三个类均定义在threads/sync.h 文件中
1、class Semaphore
信号量属于一种低级进程或线程通信原语,p()和v()操作的原子性通过开关中断来实现;
queue队列,存放wait线程,避免忙等;
class Semaphore {
public:
Semaphore(char* debugName, int initialValue); // set initial value
~Semaphore(); // de-allocate semaphore
char* getName() { return name;} // debugging assist
void P(); // these are the only operations on a semaphore
void V(); // they are both *atomic*
private:
char* name; // useful for debugging
int value; // semaphore value, always >= 0
List *queue; // threads waiting in P() for the value to be > 0
};
2、class Lock
lock是用值是一的semaphore实现的;lock是之后的条件变量的成员,isHeldByCurrentThread()为管程的实现带来便利。
class Lock {
public:
Lock(char* debugName); // initialize lock to be FREE
~Lock(); // deallocate lock
char* getName() { return name; } // debugging assist
void Acquire(); // these are the only operations on a lock
void Release(); // they are both *atomic*
bool isHeldByCurrentThread();
private:
char* name; // for debugging
Thread *owner; // remember who acquired the lock
Semaphore *lock; // use semaphore for the actual lock
};
3、class Condition
condition是管程的重要成员;它与semaphore的差别是,p和v操作,需要传一个参数lock进去。
class Condition {
public:
Condition(char* debugName); // initialize condition to
// "no one waiting"
~Condition(); // deallocate the condition
char* getName() { return (name); }
void Wait(Lock *conditionLock); // these are the 3 operations on
// condition variables; releasing the
// lock and going to sleep are
// *atomic* in Wait()
void Signal(Lock *conditionLock); // conditionLock must be held by
void Broadcast(Lock *conditionLock);// the currentThread for all of
// these operations
private:
char* name;
List* queue; // threads waiting on the condition
Lock* lock; // debugging aid: used to check correctness of
// arguments to Wait, Signal and Broacast
};
三、使用信号量解决生产者/消费者同步问题
1、实现思路与步骤
如上所述,信号量nachos已经实现;生产者消费者问题的代码框架已经给出,只需要在合适的位置,填入空缺的代码。
要填的有以下几部分:
1)初始化信号量
考虑需要什么样的信号量:
生产者之前的锁——不能同时写;消费者之间的锁——不能同时读;
buffer有货物可消费的信号量——sem_consume;buffer有空可写的信号量——sem_produce;
注意:对于buffer,有货物时,可以同时读写,所以定一个锁,使得一个时刻只能一个人访问buffer,是低效的。
mutex_produce=new Lock("mutex_produce");
mutex_consume=new Lock("mutex_consume");
sem_produce=new Semaphore("sem_produce",BUFF_SIZE);//empty
sem_consume=new Semaphore("sem_consume",0);//full
2)生产者,消费者线程创建
Thread *t = new Thread(prod_names[i]);
t->Fork(Producer, i);
3)生产者函数控制
message=new slot(which,num);
sem_produce->P();
mutex_produce->Acquire();
ring->Put(message);
mutex_produce->Release();
sem_consume->V();
4)消费者函数控制
sem_consume->P();
mutex_consume->Acquire();
ring->Get(message);
mutex_consume->Release();
sem_produce->V();
2、结果展示
四、使用管程解决生产者/消费者同步问题
1、实现思路与步骤
用管程解决该问题,不能套用一给出的代码框架。
需要自己利用condition和lock,根据管程思想,写一个Ring类,这样,访问ring的人只需要简单的调用put,get函数,不需要考虑同步细节。
1)管程类Ring2
class Ring2 {
public:
Ring2(int sz); // Constructor: initialize variables, allocate space.
~Ring2(); // Destructor: deallocate space allocated above.
void Put(slot *message); // Put a message the next empty slot.
void Get(slot *message); // Get a message from the next full slot.
int Full(); // Returns non0 if the ring is full, 0 otherwise.
int Empty(); // Returns non0 if the ring is empty, 0 otherwise.
private:
int size; // The size of the ring buffer.
int in, out; // Index of Put and Get
slot *buffer; // A pointer to an array for the ring buffer.
int current; // the current number of full slots in the buffer
Condition *notfull; // condition variable to wait until not full
Condition *notempty; // condition variable to wait until not empty
Lock *mutex; //semaphore for the mutual exclusion
//Semaphore *next; //semaphore for "next" queue
//int next_count; // the number of threads in "next" queue
};
具体实现:
2)使用管程Ring2的生产者,消费者程序
ring2=new Ring2(BUFF_SIZE);
void Producer2(_int which)
{
int num;
slot *message;
for (num = 0; num < N_MESSG ; num++) {
// the code to prepare the message goes here.
message=new slot(which,num);
ring2->Put(message);
}
}
//对于消费者的过程:
void Consumer2(_int which)
{
char str[MAXLEN];
char fname[LINELEN];
int fd;
slot *message = new slot(0,0);
sprintf(fname, "tmp_%d", which);
printf("file name is %s \n", fname);
if ( (fd = creat(fname, 0600) ) == 1)
{
perror("creat: file create failed");
return;
}
for (; ; ) {
ring2->Get(message);
sprintf(str,"producer id > %d; Message number > %d;\n",
message->thread_id,
message->value);
if ( write(fd, str, strlen(str)) == 1 ) {
perror("write: write failed");
return;
}
}
}
2、结果展示