c++读写锁--读者写者问题

又名:共享-互斥锁,多读者-单写者锁。

允许多个读者进入临界区,因为只读数据是安全的;

只允许单写者进入临界区。

所以,读者进入临界区时,第一个读者对临界区资源加一次锁,最后一个读者解锁(所以又名:共享锁,读锁);写者进入临界区时,每次都需要对临界区加锁解锁(又名:排他锁,写锁)。

注:semaphore信号量的值为1,等同于互斥量使用。

第一种实现,读优先

 1 semaphore resource=1;
 2 semaphore rmutex=1;
 3 readcount=0;
 4 
 5 /*
 6    resource.P() is equivalent to wait(resource)
 7    resource.V() is equivalent to signal(resource)
 8    rmutex.P() is equivalent to wait(rmutex)
 9    rmutex.V() is equivalent to signal(rmutex)
10 */
11 
12 writer() {
13     resource.P();          //Lock the shared file for a writer
14 
15     <CRITICAL Section>
16     // Writing is done
17 
18     <EXIT Section>
19     resource.V();          //Release the shared file for use by other readers. Writers are allowed if there are no readers requesting it.
20 }
21 
22 reader() {
23     rmutex.P();           //Ensure that no other reader can execute the <Entry> section while you are in it
24     <CRITICAL Section>
25     readcount++;          //Indicate that you are a reader trying to enter the Critical Section
26     if (readcount == 1)   //Checks if you are the first reader trying to enter CS
27         resource.P();     //If you are the first reader, lock the resource from writers. Resource stays reserved for subsequent readers
28     <EXIT CRITICAL Section>
29     rmutex.V();           //Release
30 
31     // Do the Reading
32 
33     rmutex.P();           //Ensure that no other reader can execute the <Exit> section while you are in it
34     <CRITICAL Section>
35     readcount--;          //Indicate that you are no longer needing the shared resource. One fewer reader
36     if (readcount == 0)   //Checks if you are the last (only) reader who is reading the shared file
37         resource.V();     //If you are last reader, then you can unlock the resource. This makes it available to writers.
38     <EXIT CRITICAL Section>
39     rmutex.V();           //Release
40 }

第一个读者进入时,将资源加锁,从而安全访问临界区,不会被写者修改;由于需要对读者计数,所以需要一个rmutex互斥量来保护readcount修改的安全。读者数为0时,释放资源锁。

第二种实现,写优先

 1 int readcount, writecount;                   //(initial value = 0)
 2 semaphore rmutex, wmutex, readTry, resource; //(initial value = 1)
 3 
 4 //READER
 5 reader() {
 6 <ENTRY Section>
 7   readTry.P();                 //Indicate a reader is trying to enter
 8   rmutex.P();                  //lock entry section to avoid race condition with other readers
 9   readcount++;                 //report yourself as a reader
10   if (readcount == 1)          //checks if you are first reader
11     resource.P();              //if you are first reader, lock  the resource
12   rmutex.V();                  //release entry section for other readers
13   readTry.V();                 //indicate you are done trying to access the resource
14 
15 <CRITICAL Section>
16 //reading is performed
17 
18 <EXIT Section>
19   rmutex.P();                  //reserve exit section - avoids race condition with readers
20   readcount--;                 //indicate you're leaving
21   if (readcount == 0)          //checks if you are last reader leaving
22     resource.V();              //if last, you must release the locked resource
23   rmutex.V();                  //release exit section for other readers
24 }
25 
26 //WRITER
27 writer() {
28 <ENTRY Section>
29   wmutex.P();                  //reserve entry section for writers - avoids race conditions
30   writecount++;                //report yourself as a writer entering
31   if (writecount == 1)         //checks if you're first writer
32     readTry.P();               //if you're first, then you must lock the readers out. Prevent them from trying to enter CS
33   wmutex.V();                  //release entry section
34   resource.P();                //reserve the resource for yourself - prevents other writers from simultaneously editing the shared resource
35 <CRITICAL Section>
36   //writing is performed
37   resource.V();                //release file
38 
39 <EXIT Section>
40   wmutex.P();                  //reserve exit section
41   writecount--;                //indicate you're leaving
42   if (writecount == 0)         //checks if you're the last writer
43     readTry.V();               //if you're last writer, you must unlock the readers. Allows them to try enter CS for reading
44   wmutex.V();                  //release exit section
45 }

写者优先,所以当第一个写者进入时,尝试对读者加锁,让后面的读者阻塞,即readTry.P();增加了writecount写者计数,不为0时,不会让读者进入,即readTry.V();写者对临界区的访问仍然需要互斥进行,即resource.P(),resource.V();wmutex是为了保护计数的。

第三种实现,读者写者都公平

 1 int readcount;                // init to 0; number of readers currently accessing resource
 2 
 3 // all semaphores initialised to 1
 4 semaphore resource;           // controls access (read/write) to the resource
 5 semaphore rmutex;             // for syncing changes to shared variable readcount
 6 semaphore serviceQueue;       // FAIRNESS: preserves ordering of requests (signaling must be FIFO)
 7 
 8 //READER
 9 reader() {
10 <ENTRY Section>
11   serviceQueue.P();           // wait in line to be serviced
12   rmutex.P();                 // request exclusive access to readcount
13   readcount++;                // update count of active readers
14   if (readcount == 1)         // if I am the first reader
15     resource.P();             // request resource access for readers (writers blocked)
16   serviceQueue.V();           // let next in line be serviced
17   rmutex.V();                 // release access to readcount
18     
19 <CRITICAL Section>
20 //reading is performed
21     
22 <EXIT Section>
23   rmutex.P();                 // request exclusive access to readcount
24   readcount--;                // update count of active readers
25   if (readcount == 0)         // if there are no readers left
26     resource.V();             // release resource access for all
27   rmutex.V();                 // release access to readcount
28 }
29 
30 //WRITER
31 writer() {
32 <ENTRY Section>
33   serviceQueue.P();           // wait in line to be serviced
34   resource.P();               // request exclusive access to resource
35   serviceQueue.V();           // let next in line be serviced
36     
37 <CRITICAL Section>
38 // writing is performed
39     
40 <EXIT Section>
41   resource.V();               // release resource access for next reader/writer
42 }

与第一种读优先比较,多了一个serviceQueue互斥锁,读者写者同时请求这个互斥锁,以此达到公平竞争的目的。

 读写锁的缺点:

上面的实现中,最少需要两个互斥变量。临界区很小的话, 读写锁可能没有直接用一个mutex快; 临界区很大又说明代码写得不好,用不用读写锁需要测试后才知道。

Reference:

https://en.wikipedia.org/wiki/Readers%E2%80%93writers_problem

https://dengzuoheng.github.io/cpp-concurency-pattern-7-rwlock

原文地址:https://www.cnblogs.com/mingbujian/p/14098693.html