且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

读/写锁实现只使用互斥?

更新时间:2023-11-13 22:45:10

您不需要为数据单独的互斥体;如果其内部逻辑正确,整个结构将用作数据锁定。相反,您可以为读者和写者使用两个单独的条件变量,以便您可以广播所有等待的阅读器,而不会影响等待写入程序。代码如下;你也可以看到这样更简单。此外,我添加了一个析构函数并修复了w_lock中的错误:等待的条件应该是(NoOfReaders!= 0 || NoOfWriters!= 0),而不是&&< / code>。

  class rw_lock_t {

int NoOfReaders;
int NoOfWriters,NoOfWritersWaiting;
pthread_mutex_t class_mutex;
pthread_cond_t reader_gate;
pthread_cond_t writer_gate;

public:

rw_lock_t()
:NoOfReaders(0),NoOfWriters(0),NoOfWritersWating(0),
class_mutex(PTHREAD_MUTEX_INITIALIZER)
reader_gate(PTHREAD_COND_INITIALIZER),
writer_gate(PTHREAD_COND_INITIALIZER)
{}
〜rw_lock_t()
{
pthread_mutex_destroy(& class_mutex);
pthread_cond_destroy(& reader_gate);
pthread_cond_destroy(& writer_gate);
}
void r_lock()
{
pthread_mutex_lock(& class_mutex);
// while(NoOfWriters> 0 || NoOfWritersWaiting> 0)// Writer Preference
while(NoOfWriter> 0)
{
pthread_cond_wait(& reader_gate,& class_mutex) ;
}
NoOfReaders ++;
pthread_mutex_unlock(& class_mutex);
}
void w_lock()
{
pthread_mutex_lock(& class_mutex);
NoOfWritersWaiting ++;
while(NoOfReaders> 0 || NoOfWriters> 0)
{
pthread_cond_wait(& writer_gate,& class_mutex);
}
NoOfWritersWaiting--; NoOfWriters ++;
pthread_mutex_unlock(& class_mutex);
}
void r_unlock()
{
pthread_mutex_lock(& class_mutex);
NoOfReaders--;
if(NoOfReaders == 0&& NoOfWritersWaiting> 0)
pthread_cond_signal(& writer_gate);
pthread_mutex_unlock(& class_mutex);
}
void w_unlock()
{
pthread_mutex_lock(& class_mutex);
NoOfWriters--;
if(NoOfWritersWaiting> 0)
pthread_cond_signal(& writer_gate);
// else // Writer Preference - 除非没有写者,否则不发信号读取
pthread_cond_broadcast(& reader_gate);
pthread_mutex_unlock(& class_mutex);
}
};


I was trying to implement read/write lock using mutex only (just for learning). Just when i thought i have covered all corner cases (as the program worked with variety of combinations), i have realized, i ignored the fact (as it worked in ubuntu) that, the mutex should be freed by the owner of the thread. Below is my implementation,

class rw_lock_t{

    int NoOfReaders;
    int NoOfWriters, NoOfWritersWaiting;
    pthread_mutex_t class_mutex;
    pthread_cond_t class_cond;
    pthread_mutex_t data_mutex;

public:

    rw_lock_t()
    : NoOfReaders(0),
      NoOfWriters(0), NoOfWritersWaiting(0)
    {
            pthread_mutex_init(&class_mutex, NULL);
            pthread_mutex_init(&data_mutex, NULL);
            pthread_cond_init(&class_cond, NULL);
    }
    void r_lock()
    {
            pthread_mutex_lock(&class_mutex);
            //while(NoOfWriters!=0 || NoOfWritersWaiting!=0) //Writer Preference
            while(NoOfWriters!=0)
            {
                    pthread_cond_wait(&class_cond, &class_mutex);
            }
            if(NoOfReaders==0)
            {
                    pthread_mutex_unlock(&class_mutex);
                    pthread_mutex_lock(&data_mutex);
                    pthread_mutex_lock(&class_mutex);
                    NoOfReaders++;
                    pthread_mutex_unlock(&class_mutex);
            }
            else if(NoOfReaders>0) //Already Locked
            {
                    NoOfReaders++;
                    pthread_mutex_unlock(&class_mutex);
            }
    }
    void w_lock()
    {
            pthread_mutex_lock(&class_mutex);
            NoOfWritersWaiting++;
            while(NoOfReaders!=0 && NoOfWriters!=0)
            {
                    pthread_cond_wait(&class_cond, &class_mutex);
            }
            pthread_mutex_unlock(&class_mutex);

            pthread_mutex_lock(&data_mutex);
            pthread_mutex_lock(&class_mutex);
            NoOfWritersWaiting--; NoOfWriters++;
            pthread_mutex_unlock(&class_mutex);
    }
    void r_unlock()
    {
            pthread_mutex_lock(&class_mutex);
            NoOfReaders--;
            if(NoOfReaders==0)
                    pthread_mutex_unlock(&data_mutex);
            pthread_mutex_unlock(&class_mutex);
            pthread_cond_signal(&class_cond);
    }
    void w_unlock()
    {
            pthread_mutex_lock(&class_mutex);
            NoOfWriters--;
            if(NoOfWriters==0)
                    pthread_mutex_unlock(&data_mutex);
            pthread_mutex_unlock(&class_mutex);
            pthread_cond_signal(&class_cond);
    }
};

My question now is, what is the best way (minimal change) to rectify. Semaphore is definitely the idle choice, but I thought of solutions as below

Solution#1

1) I will have a dedicated thread, just to lock/unlock the mutex for read case.

2) This thread will be waiting on a condition variable to get signal from r_lock or r_unlock.

3) r_lock and r_unlock will instead of doing "pthread_mutex_lock/unlock(&data_mutex);", will signal the dedicated thread to lock instead.

4) I have to remember many facts for this implementation,

  • The signaling and actual locking are two different events, so might need synchronization.

  • Will need a mutex+condVariable+thread and more synchronization extra.

Update: Solution#2

1) The thread who did the actual locking will keep its tid globally.

2) whenever a thread unlocks will make sure the check equality with the global tid.

3) If matches will wait for "NoOfReaders==0" condition and unlock it.

So, is there a better way in which the program can be rectified.

You do not need a separate mutex "for data"; the whole construct will serve as the data lock, if its internal logic is correct. Instead, you could use two separate condition variables for readers and for writers, so that you can broadcast all waiting readers without affecting waiting writers. The code is below; you can also see that it's simpler this way. Besides, I added a destructor and fixed a bug in w_lock: the condition to wait should be (NoOfReaders!=0 || NoOfWriters!=0), and not &&.

class rw_lock_t {

    int NoOfReaders;
    int NoOfWriters, NoOfWritersWaiting;
    pthread_mutex_t class_mutex;
    pthread_cond_t  reader_gate;
    pthread_cond_t  writer_gate;

public:

    rw_lock_t()
    : NoOfReaders(0), NoOfWriters(0), NoOfWritersWating(0),
      class_mutex(PTHREAD_MUTEX_INITIALIZER),
      reader_gate(PTHREAD_COND_INITIALIZER),
      writer_gate(PTHREAD_COND_INITIALIZER)
    {}
    ~rw_lock_t()
    {
        pthread_mutex_destroy(&class_mutex);
        pthread_cond_destroy(&reader_gate);
        pthread_cond_destroy(&writer_gate);
    }
    void r_lock()
    {
        pthread_mutex_lock(&class_mutex);
        //while(NoOfWriters>0 || NoOfWritersWaiting>0) //Writer Preference
        while(NoOfWriters>0)
        {
            pthread_cond_wait(&reader_gate, &class_mutex);
        }
        NoOfReaders++;        
        pthread_mutex_unlock(&class_mutex);
    }
    void w_lock()
    {
        pthread_mutex_lock(&class_mutex);
        NoOfWritersWaiting++;
        while(NoOfReaders>0 || NoOfWriters>0)
        {
            pthread_cond_wait(&writer_gate, &class_mutex);
        }
        NoOfWritersWaiting--; NoOfWriters++;
        pthread_mutex_unlock(&class_mutex);
    }
    void r_unlock()
    {
        pthread_mutex_lock(&class_mutex);
        NoOfReaders--;
        if(NoOfReaders==0 && NoOfWritersWaiting>0)
            pthread_cond_signal(&writer_gate);
        pthread_mutex_unlock(&class_mutex);
    }
    void w_unlock()
    {
        pthread_mutex_lock(&class_mutex);
        NoOfWriters--;
        if(NoOfWritersWaiting>0)
            pthread_cond_signal(&writer_gate);
        //else //Writer Preference - don't signal readers unless no writers
        pthread_cond_broadcast(&reader_gate);
        pthread_mutex_unlock(&class_mutex);
    }
};