读者写者模型---读优先与写优先

Linux线程中写过生产者消费者模型,这次研究读者写者模型。


读者写者模型遵循的规则

读者-写者模型同样遵循321规则:

  • 写-写互斥,即不能有两个写者同时进行写操作。
  • 读-写互斥,即不能同时有一个线程在读,而另一个线程在写。
  • 读-读允许,读者和读者之间没有关系,即可以有一个或多个读者同时读。

读者优先

如果一个读者申请进行读操作时已有另一个读者正在进行读操作,则该读者可直接开始读操作。而此时写者正在被阻塞,只有所有的读者都读完,写者才会被唤醒。

//读者优先
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#include <iostream>
using namespace std;

struct data 
{
	string str;
};

//临界区数据,读者写者分别读和写入shareData
data shareData;

//读优先保证当已经有读者在读时,后续的读者可以直接开始读操作
//当然,写者必须等没有读者读时才能写
sem_t mutex_readCount, mutex_write;
//读者的数量
int  readCount;

//读者
void* Reader(void* param) 
{
	printf("线程 %ud: 正在等待读\n",pthread_self());
	int readWait = rand()%5;	
	sleep(readWait);	

	//读者数量++
	sem_wait(&mutex_readCount);
	readCount++;
	if(readCount == 1)
	{
		//当读者数量>0时,写者不能写
		sem_wait(&mutex_write);	
	}
	sem_post(&mutex_readCount);
	
	//读者进行读操作
	printf("线程 %ud: 开始进行读\n", pthread_self());
	cout<<"读者读出数据:"<<shareData.str<<endl;;
	printf("线程 %ud: 读取完毕\n", pthread_self());
	//读完以后,读者数量--
    
	sem_wait(&mutex_readCount);
	readCount--;
	if(readCount == 0)
	{
        //读者数量为0,写者可以写了
		sem_post(&mutex_write);
	}
	sem_post(&mutex_readCount);

	pthread_exit(0);
}

//写者
void* Writer(void* param) 
{
	printf("线程 %ud: 正在等待写\n",pthread_self());
	int writeWait = rand()%5;
	sleep(writeWait);

	string& writeStr= ((data*)param)->str;

	//多个线程写时,使用mutex_write进行互斥
	sem_wait(&mutex_write);
	printf("线程 %ud: 开始写入:%s\n",pthread_self(),writeStr.c_str());	
	
	shareData.str=writeStr;

	printf("线程 %ud: 写完\n",pthread_self());	
	sem_post(&mutex_write);
	
	pthread_exit(0);
}

int main() 
{
	srand((unsigned)time(NULL));
	pthread_t tid1;
	pthread_t tid2;
	pthread_t tid3;
	pthread_t tid4;
	pthread_t tid5;

	readCount=0;
	shareData.str="默认值";

    sem_init(&mutex_readCount, 0, 1);
    sem_init(&mutex_write, 0, 1);

    readCount = 0;
	//创建几个线程,分别为写者和读者,进行测试
	pthread_create(&tid2, NULL, Reader, NULL);

	data w1;
	w1.str="abc";
	pthread_create(&tid1, NULL, Writer, (void*)&w1);

	pthread_create(&tid4, NULL, Reader, NULL);
	pthread_create(&tid5, NULL, Reader, NULL);
	data w2;
	w2.str="1234";
	pthread_create(&tid3, NULL, Writer, (void*)&w2);

	sem_destroy(&mutex_readCount);
	sem_destroy(&mutex_write);

	pthread_join(tid1,NULL);
	pthread_join(tid2,NULL);
	pthread_join(tid3,NULL);
	pthread_join(tid4,NULL);
	pthread_join(tid5,NULL);

	return 0;
}

在这里插入图片描述


写者优先

如果一个读者申请进行读操作时已有另一写者在等待访问共享资源,则该读者必须等到没有写者处于等待状态后才能开始读操作。

换句话说,如果一个读者A申请进行读操作,并且写者想进行写操作,此时A会与写者竞争锁,如果写者竞争成功则写者先写,后续来的写者由于多线程原因会处于等待状态(等前一个写者写完),只有当等待状态的写者数量为0时,读者A才可以进行读。
如果读者A竞争成功则A先读,后续的读者B仍然要与写者竞争。

//写者优先
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#include <iostream>
using namespace std;

struct data 
{
	string str;
};

//临界区数据,读者写者分别读和写入shareData
data shareData;

//写优先必须保证必须等写者都写完,读者才能读,所以用rwMutex来控制其关系
//用mutex_write信号量来控制写者和写者之间互斥关系,当有读者读时,写者不能写
//由于读者和读者之间没有关系,所以不需要锁
//mutex_writeCount和mutex_readCount分别对读者和写者数量进行原子操作
sem_t rwMutex, mutex_writeCount, mutex_readCount, mutex_write;
//写者的数量和读者的数量
int writeCount, readCount;
//mutex主要用处是避免写者同时与多个读者进行竞争,读者中信号量RWMutex比mutex先释放,则一旦有写者,写者可马上获得资源RWMutex
sem_t mutex;

//读者
void* Reader(void* param) 
{
	printf("线程 %ud: 正在等待读\n",pthread_self());
	int readWait = rand()%5;
	sleep(readWait);	
	//读者获取rwMutex信号量,
	//读优先必须保证必须等写者都写完,读者才能读,所以写者先获取rwMutex,当全部写完后才释放
	//如果写者没释放读者就来读,读者会被挂起

	sem_wait(&mutex);
	sem_wait(&rwMutex);
	//读者数量++
	sem_wait(&mutex_readCount);
	readCount++;
	if(readCount == 1)
	{
		//当读者数量>0时,写者不能写
		sem_wait(&mutex_write);	
	}
	sem_post(&mutex_readCount);
	//到这里,读者申请读成功,立刻释放rwMutex,这样后续的读者和写者就能竞争rwMutex了
	//并且由于mutex,写者可以避免和多个读者竞争rwMutex
	sem_post(&rwMutex);
	sem_post(&mutex);

	printf("线程 %ud: 开始进行读\n", pthread_self());
	//读者进行读操作
	cout<<"读者读出数据:"<<shareData.str<<endl;;

	printf("线程 %ud: 读取完毕\n", pthread_self());
	//读完以后,读者数量--
	sem_wait(&mutex_readCount);
	readCount--;
	if(readCount == 0)
	{
		sem_post(&mutex_write);
	}
	sem_post(&mutex_readCount);

	pthread_exit(0);
}

//写者
void* Writer(void* param) 
{
	printf("线程 %ud: 正在等待写\n",pthread_self());
	int writeWait = rand()%5;
	sleep(writeWait);

	string& writeStr= ((data*)param)->str;	

	sem_wait(&mutex_writeCount);
	writeCount++;
	writeWait++;
	if(writeCount == 1)
	{
		//已经有一个写者,获取rwMutex,当写者数量为0时释放它,读者才能读
		sem_wait(&rwMutex);
	}
	sem_post(&mutex_writeCount);
	//多个线程写时,使用mutex_write进行互斥
	sem_wait(&mutex_write);
	printf("线程 %ud: 开始写入:%s\n",pthread_self(),writeStr.c_str());	
	shareData.str=writeStr;

	printf("线程 %ud: 写完\n",pthread_self());	
	sem_post(&mutex_write);

	sem_wait(&mutex_writeCount);
	writeCount--;
	if(writeCount == 0) 
	{
		sem_post(&rwMutex);
	}
	sem_post(&mutex_writeCount);
	
	pthread_exit(0);
}

int main() 
{
	srand((unsigned)time(NULL));

	pthread_t tid1;
	pthread_t tid2;
	pthread_t tid3;
	pthread_t tid4;
	pthread_t tid5;

	writeCount=readCount=0;
	shareData.str="默认值";

    sem_init(&rwMutex, 0, 1);
    sem_init(&mutex_writeCount, 0, 1);
    sem_init(&mutex_readCount, 0, 1);
    sem_init(&mutex_write, 0, 1);
    sem_init(&mutex, 0, 1);

	//创建几个线程,分别为写者和读者,进行测试
	pthread_create(&tid2, NULL, Reader, NULL);

	data w1;
	w1.str="abc";
	pthread_create(&tid1, NULL, Writer, (void*)&w1);

	pthread_create(&tid4, NULL, Reader, NULL);
	pthread_create(&tid5, NULL, Reader, NULL);
	data w2;
	w2.str="1234";
	pthread_create(&tid3, NULL, Writer, (void*)&w2);

	sem_destroy(&rwMutex);
	sem_destroy(&mutex_writeCount);
	sem_destroy(&mutex_readCount);
	sem_destroy(&mutex_write);
	sem_destroy(&mutex);


	pthread_join(tid1,NULL);
	pthread_join(tid2,NULL);
	pthread_join(tid3,NULL);
	pthread_join(tid4,NULL);
	pthread_join(tid5,NULL);

	return 0;
}

在这里插入图片描述


读者和写者公平竞争

上面两种方式都会导致读者或者写者饥饿,即读者一直读或者写者一直写的情况。下面解决这个问题,采取的策略是:

  • 读者和写者优先级相同;
  • 写者、读者互斥访问;
  • 只能⼀个写者访问临界区;
  • 可以有多个读者同时访问临街资源
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#include <iostream>
using namespace std;

struct data 
{
	string str;
};

//临界区数据,读者写者分别读和写入shareData
data shareData;

//读优先保证当已经有读者在读时,后续的读者可以直接开始读操作
//当然,写者必须等没有读者读时才能写
sem_t mutex_readCount, mutex_write;
//flag实现公平竞争
sem_t flag;

//读者的数量
int  readCount;

//读者
void* Reader(void* param) 
{
	printf("线程 %ud: 正在等待读\n",pthread_self());
	int readWait = rand()%5;	
	sleep(readWait);	

    //在读之前,先和写者竞争flag
    sem_wait(&flag);
	//读者数量++
	sem_wait(&mutex_readCount);
	readCount++;
	if(readCount == 1)
	{
		//当读者数量>0时,写者不能写
		sem_wait(&mutex_write);	
	}
	sem_post(&mutex_readCount);
    sem_post(&flag);

	
	//读者进行读操作
	printf("线程 %ud: 开始进行读\n", pthread_self());
	cout<<"读者读出数据:"<<shareData.str<<endl;;
	printf("线程 %ud: 读取完毕\n", pthread_self());
	//读完以后,读者数量--
    
	sem_wait(&mutex_readCount);
	readCount--;
	if(readCount == 0)
	{
        //读者数量为0,写者可以写了
		sem_post(&mutex_write);
	}
	sem_post(&mutex_readCount);

	pthread_exit(0);
}

//写者
void* Writer(void* param) 
{
	printf("线程 %ud: 正在等待写\n",pthread_self());
	int writeWait = rand()%5;
	sleep(writeWait);

	string& writeStr= ((data*)param)->str;

    //在写之前,先和读者竞争flag
    sem_wait(&flag);

	//多个线程写时,使用mutex_write进行互斥
	sem_wait(&mutex_write);
	printf("线程 %ud: 开始写入:%s\n",pthread_self(),writeStr.c_str());	
	
	shareData.str=writeStr;

	printf("线程 %ud: 写完\n",pthread_self());	
	sem_post(&mutex_write);
    sem_post(&flag);

	
	pthread_exit(0);
}

int main() 
{
	srand((unsigned)time(NULL));
	pthread_t tid1;
	pthread_t tid2;
	pthread_t tid3;
	pthread_t tid4;
	pthread_t tid5;

	readCount=0;
	shareData.str="默认值";

    sem_init(&mutex_readCount, 0, 1);
    sem_init(&mutex_write, 0, 1);
    sem_init(&flag, 0, 1);


    readCount = 0;
	//创建几个线程,分别为写者和读者,进行测试
	pthread_create(&tid2, NULL, Reader, NULL);

	data w1;
	w1.str="abc";
	pthread_create(&tid1, NULL, Writer, (void*)&w1);

	pthread_create(&tid4, NULL, Reader, NULL);
	pthread_create(&tid5, NULL, Reader, NULL);
	data w2;
	w2.str="1234";
	pthread_create(&tid3, NULL, Writer, (void*)&w2);

	sem_destroy(&mutex_readCount);
	sem_destroy(&mutex_write);

	pthread_join(tid1,NULL);
	pthread_join(tid2,NULL);
	pthread_join(tid3,NULL);
	pthread_join(tid4,NULL);
	pthread_join(tid5,NULL);

	return 0;
}

对⽐读者优先策略,可以发现,读者优先中只要后续有读者到达,读者就可以进⼊读者队列, ⽽写者必须等待,直到没有读者到达。
没有读者到达会导致读者队列为空,即 readCount ==0,此时写者才可以进⼊临界区执⾏写操作。⽽这⾥ flag 的作⽤就是阻⽌读者的这种特殊权限(特殊权限是只要读者到达,就可以进⼊读者队列)。

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
当然,我可以帮你一个N-gram模型。下面是一个使用Python编的简单的N-gram模型的例子: ```python from collections import defaultdict from nltk import ngrams def build_ngram_model(corpus, n): model = defaultdict(list) for sentence in corpus: tokens = sentence.split() # 使用ngrams函数生成N-gram序列 ngrams_sequence = list(ngrams(tokens, n)) for grams in ngrams_sequence: prefix = ' '.join(grams[:-1]) suffix = grams[-1] model[prefix].append(suffix) return model def generate_sentence(model, n, start_word, max_length): current_word = start_word sentence = [current_word] for _ in range(max_length): prefix = ' '.join(sentence[-n+1:]) if prefix in model: next_word = random.choice(model[prefix]) sentence.append(next_word) else: break return ' '.join(sentence) # 示例用法 corpus = [ "I love coding", "Coding is fun", "I love programming" ] model = build_ngram_model(corpus, 2) generated_sentence = generate_sentence(model, 2, "I", 10) print(generated_sentence) ``` 上述代码首先定义了一个`build_ngram_model`函数,用于从给定的语料库中构建N-gram模型。它使用`ngrams`函数从每个句子中生成N-gram序列,并将前缀和后缀存储在一个字典中。 然后,我们定义了一个`generate_sentence`函数,用于生成基于N-gram模型的句子。它从给定的起始词开始,根据模型中的前缀和后缀进行随机选择,生成一个句子。 在示例用法中,我们提供了一个简单的语料库,并使用N-gram模型生成一个以"I"开头的句子。 请注意,这只是一个简单的示例,实际应用中可能需要处理更大的语料库和更复杂的文本预处理。希望这个例子能帮到你!如果有任何问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

今天也要写bug、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值