函数的可重入性(reentrant)及线程安全

这篇博客对应线上课程的第六次实验。

网易云课堂:http://mooc.study.163.com/learn/USTC-1000002006?tid=1000081002#/learn/announce
实验楼:https://www.shiyanlou.com/courses/122
代码库:http://git.shiyanlou.com/chenxu/shiyanlou_cs122

实验目的

       要写一个线程安全的链表,为LinkTable添加互斥锁,完成“读写互斥”、“单写多读”的目的。

实验思路

互斥锁原理

       首先要了解一下互斥锁的原理。在线程实际运行过程中,需要对多个线程操作的“共享空间”保持同步,这时可以用互斥锁来完成任务。

想进一步了解互斥锁的可以看看这篇博客:
http://blog.csdn.net/harry_lyc/article/details/6222920

读者写者问题

       读者一写者问题是一个用信号量实现的经典进程同步问题。在系统中,一个数据集( 如文件或记录) 被几个并发进程共享,这些线程分两类,一部分只要求进行复操作,称之为“读者”;另一类要求写或修改操作,我们称之为“写者“。
       读者优先的算法是一种解决办法:当没有写进程正在访问共享数据集时,读进程可以进入访问,否则必须等待。而读者优先的算法存在“饿死写者”线程的问题:只要有读者不断到来,写者就要持久地等待,直到所有的读者都读完且没有新的读者到来时写者才能写数据集。
       在写者优先算法中,我们要实现的目标是:
        1. 要让读者与写者之间、以及写者与写者之问要互斥地访同数据集;
        2. 在无写进程到来时各读者可同时访问数据集;
        3. 在读者和写者都等待时访问时写者优先.

想进一步了解读者写者问题的可以看看这几篇博客:
http://blog.csdn.net/cz_hyf/article/details/4443551

实验过程

实现部分借鉴了这位博主的内容 http://blog.csdn.net/yaozhiyi/article/details/7563869

互斥量

在Linktable内添加一系列互斥锁:

struct LinkTable
{
    tLinkTableNode *pHead;
    tLinkTableNode *pTail;
    int SumOfNode;
    int readerCnt;
    int writerCnt;  
    pthread_mutex_t accessReaderCnt;  
    pthread_mutex_t accessWriterCnt;  
    pthread_mutex_t writeLock;  
    pthread_mutex_t readerLock;  
    pthread_mutex_t outerLock;  
};
  • readerCnt:统计读者的数量,用于并发读取;
  • writerCnt:统计写者的数量,用于控制
  • accessReaderCnt:互斥各线程对readerCnt的访问;
  • accessWriterCnt:互斥各线程对writerCnt的访问;
  • writeLock:限制只有一个写者修改数据;
  • readerLock:写者优先,用于限制所有读者的锁;
  • outerLock:在一个读者到来时,阻塞后续读者,使写者有获取readerLock的优先性。

       CreateLinkTable和DeleteLinkTable两个函数的修改很简单,只是加入了初始化互斥量和销毁互斥量的操作。主要的操作还是在AddLinkTableNode、DelLinkTableNode、SearchLinkTableNode和GetNextLinkTableNode这4个函数中,其中前两个为写操作,后两个为读操作。

写操作

       在这里,使用AddLinkTableNode来进行说明读写互斥与写者优先的实现。
       首先,在获得写操作互斥锁之前,先获取accessWriterCnt锁,来控制写者并发时不发生冲突;之后对写者数加1,且当第一个写者到来时对readerLock进行加锁,阻塞之后到来的所有读者。

pthread_mutex_lock(&(pLinkTable->accessWriterCnt));
{//临界区,希望修改 writerCnt,独占 writerCnt
    pLinkTable->writerCnt++;
    if(pLinkTable->writerCnt == 1){
        //阻止后续的读者加入待读队列
        pthread_mutex_lock(&(pLinkTable->readerLock));
    }
}
pthread_mutex_unlock(&(pLinkTable->accessWriterCnt));

       在释放accessWriterCnt锁之后,本写者进行写操作,其他写者可以进入队列等待writeLock以获得写权限。

pthread_mutex_lock(&(pLinkTable->writeLock));
{//临界区,限制只有一个写者修改数据
    //write();
    if (pLinkTable->pHead == NULL)
    {
        pLinkTable->pHead = pNode;
    }
    if (pLinkTable->pTail == NULL)
    {
        pLinkTable->pTail = pNode;
    }
    else
    {
        pLinkTable->pTail->pNext = pNode;
        pLinkTable->pTail = pNode;
    }
    pLinkTable->SumOfNode += 1;
}
pthread_mutex_unlock(&(pLinkTable->writeLock));

       最后,判断一下写者队列,队列为空时再释放readerLock。

pthread_mutex_lock(&(pLinkTable->accessWriterCnt));
{//临界区,希望修改 pLinkTable->writerCnt,独占 pLinkTable->writerCnt
    pLinkTable->writerCnt--;
    if(pLinkTable->writerCnt == 0){
        //阻止后续的读者加入待读队列
        pthread_mutex_unlock(&(pLinkTable->readerLock));
    }
}
pthread_mutex_unlock(&(pLinkTable->accessWriterCnt));

读操作

       使用SearchLinkTableNode来说明读操作的实现。
       首先对outerLock进行加锁,后续读者等待,在释放outerLock之前先释放readerLock,这时写者可获取readerLock,而其他读者需要等待outerLock;第一个到来的读者对writeLock进行加锁,防止在读取时写者进行更改。

pthread_mutex_lock(&(pLinkTable->outerLock)); //假如写者锁定了readerLock,那么成千上万的读者被锁在这里
{//临界区
    pthread_mutex_lock(&(pLinkTable->readerLock));//只被一个读者占有
    {//临界区
        pthread_mutex_lock(&(pLinkTable->accessReaderCnt));//代码段 1
        {//临界区
            pLinkTable->readerCnt++;
            if(pLinkTable->readerCnt == 1){
                pthread_mutex_lock(&(pLinkTable->writeLock));
            }
        }
        pthread_mutex_unlock(&(pLinkTable->accessReaderCnt));
    }
    pthread_mutex_unlock(&(pLinkTable->readerLock));//释放时,写者将优先获得readerLock
}
pthread_mutex_unlock(&(pLinkTable->outerLock));

       查找成功的情况下(读取完成),判断读者队列,队列为空时释放writeLock。

tLinkTableNode * pNode = pLinkTable->pHead;
while (pNode != NULL)
{
    if (Conditon(pNode, args) == SUCCESS)
    {
        pthread_mutex_lock(&(pLinkTable->accessReaderCnt));//代码段2
        {//临界区
            pLinkTable->readerCnt--;
            if(pLinkTable->readerCnt == 0){
                pthread_mutex_unlock(&(pLinkTable->writeLock));//在最后一个并发读者读完这里开始禁止写者执行写操作
            }
        }
        pthread_mutex_unlock(&(pLinkTable->accessReaderCnt));
        return pNode;
    }
    pNode = pNode->pNext;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值