Reader/Writer ล็อคใน C ++

ฉันกำลังมองหาตัวล็อคผู้อ่าน / นักเขียนที่ดีใน C ++ เรามีกรณีการใช้งานของผู้เขียนไม่บ่อยเพียงคนเดียวและผู้อ่านบ่อยจำนวนมาก และต้องการเพิ่มประสิทธิภาพเพื่อสิ่งนี้ จะดีกว่า ฉันต้องการโซลูชันข้ามแพลตฟอร์ม อย่างไรก็ตาม Windows มีเพียงระบบเดียวเท่านั้นที่ยอมรับได้


person Matt Price    schedule 28.10.2008    source แหล่งที่มา


คำตอบ (12)


boost::thread เวอร์ชันใหม่กว่ามีการอ่าน/เขียนแล้ว ล็อค (1.35.0 และใหม่กว่า เห็นได้ชัดว่าเวอร์ชันก่อนหน้าทำงานไม่ถูกต้อง)

พวกเขามีชื่อ shared_lock , unique_lock และ upgrade_lock และดำเนินการบน shared_mutex.

person Greg Rogers    schedule 28.10.2008
comment
เราอยู่ที่ 1.34.1 นั่นอาจเป็นเหตุผลที่ต้องเลื่อนระดับ :) - person Matt Price; 28.10.2008
comment
ไลบรารีเธรดได้รับการยกเครื่องครั้งใหญ่ตั้งแต่ 1.34 - 1.35 ดังนั้นโปรดระวังสิ่งนี้เมื่อทำการอัพเกรด มีการเปลี่ยนแปลง API บางอย่างเกิดขึ้น แต่จะไม่ส่งผลกระทบต่อคนส่วนใหญ่มากนัก เวอร์ชันใหม่นี้สอดคล้องกับอินเทอร์เฟซไลบรารี C++0x ที่เสนอมากกว่า ซึ่งเป็นสิ่งที่ดีในระยะยาว - person Greg Rogers; 28.10.2008
comment
เราไม่ได้ใช้ไลบรารีเธรดส่วนใหญ่มากนัก แต่เราใช้สำหรับล็อคของเรา ฉันเจาะลึกรายละเอียดเพื่อเชี่ยวชาญด้าน lock_ops ในที่เดียว เพื่อให้ฉันสามารถใช้ scoped_lock กับประเภทของฉันได้ ฉันยอมรับว่าในระยะยาวเราต้องการการใช้งานที่เป็นมาตรฐานมากขึ้น - person Matt Price; 28.10.2008
comment
หากคุณโชคดีพอที่จะมี C++14 หรือ C++17 คุณสามารถใช้ shared_timed_mutex และ shared_lock ที่ตรงกัน - person Jan Kundrát; 22.10.2018

ตั้งแต่ C ++ 17 (VS2015) คุณสามารถใช้มาตรฐาน:

#include <shared_mutex>

typedef std::shared_mutex Lock;
typedef std::unique_lock< Lock >  WriteLock;
typedef std::shared_lock< Lock >  ReadLock;

Lock myLock;

void ReadFunction()
{
     ReadLock r_lock(myLock);
     //Do reader stuff
}

void WriteFunction()
{
     WriteLock w_lock(myLock);
     //Do writer stuff
}

สำหรับคอมไพเลอร์เวอร์ชันเก่าและมาตรฐาน คุณสามารถใช้ boost เพื่อสร้างการล็อกการอ่าน-เขียน:

#include <boost/thread/locks.hpp>
#include <boost/thread/shared_mutex.hpp>

typedef boost::shared_mutex Lock;
typedef boost::unique_lock< Lock >  WriteLock;
typedef boost::shared_lock< Lock >  ReadLock;
person Yochai Timmer    schedule 23.06.2011
comment
ดูเหมือนว่าสิ่งนี้จะยังคงทำให้เธรดการเขียนอดอยากเนื่องจากมีเธรดการอ่านจำนวนมากและเธรดการเขียนหนึ่งเธรด - person Brian Yeh; 16.11.2020

การใช้สิ่งที่สร้างไว้ล่วงหน้าแบบมาตรฐานที่ทดสอบไว้ล่วงหน้านั้นดีเสมอ (เช่น Boost เป็นคำตอบอื่นที่แนะนำ) แต่นี่เป็นสิ่งที่ไม่ยากเกินไปที่จะสร้างด้วยตัวเอง นี่คือการใช้งานเล็ก ๆ น้อย ๆ ที่โง่เขลาที่ดึงออกมาจากโครงการของฉัน:

#include <pthread.h>

struct rwlock {
    pthread_mutex_t lock;
    pthread_cond_t read, write;
    unsigned readers, writers, read_waiters, write_waiters;
};

void reader_lock(struct rwlock *self) {
    pthread_mutex_lock(&self->lock);
    if (self->writers || self->write_waiters) {
        self->read_waiters++;
        do pthread_cond_wait(&self->read, &self->lock);
        while (self->writers || self->write_waiters);
        self->read_waiters--;
    }
    self->readers++;
    pthread_mutex_unlock(&self->lock);
}

void reader_unlock(struct rwlock *self) {
    pthread_mutex_lock(&self->lock);
    self->readers--;
    if (self->write_waiters)
        pthread_cond_signal(&self->write);
    pthread_mutex_unlock(&self->lock);
}

void writer_lock(struct rwlock *self) {
    pthread_mutex_lock(&self->lock);
    if (self->readers || self->writers) {
        self->write_waiters++;
        do pthread_cond_wait(&self->write, &self->lock);
        while (self->readers || self->writers);
        self->write_waiters--;
    }
    self->writers = 1;
    pthread_mutex_unlock(&self->lock);
}

void writer_unlock(struct rwlock *self) {
    pthread_mutex_lock(&self->lock);
    self->writers = 0;
    if (self->write_waiters)
        pthread_cond_signal(&self->write);
    else if (self->read_waiters)
        pthread_cond_broadcast(&self->read);
    pthread_mutex_unlock(&self->lock);
}

void rwlock_init(struct rwlock *self) {
    self->readers = self->writers = self->read_waiters = self->write_waiters = 0;
    pthread_mutex_init(&self->lock, NULL);
    pthread_cond_init(&self->read, NULL);
    pthread_cond_init(&self->write, NULL);
}

pthreads ไม่ใช่ Windows จริงๆ แต่แนวคิดทั่วไปอยู่ที่นี่ การใช้งานนี้มีอคติต่อนักเขียนเล็กน้อย (นักเขียนจำนวนมากสามารถทำให้ผู้อ่านอดอยากได้อย่างไม่มีกำหนด) เพียงแก้ไข writer_unlock หากคุณต้องการให้ความสมดุลเป็นอย่างอื่น

ใช่ นี่คือ C ไม่ใช่ C++ การแปลเป็นแบบฝึกหัดที่เหลือสำหรับผู้อ่าน

แก้ไข

Greg Rogers ชี้ให้เห็นว่ามาตรฐาน POSIX ระบุ pthread_rwlock_* สิ่งนี้ไม่ได้ช่วยอะไรถ้าคุณไม่มี pthreads แต่มันทำให้ฉันนึกถึง: Pthreads-w32 ควรจะได้ผล! แทนที่จะย้ายรหัสนี้เป็น non-pthreads สำหรับการใช้งานของคุณเอง เพียงใช้ Pthreads-w32 บน Windows และเนทิฟ pthreads ทุกที่

person ephemient    schedule 28.10.2008
comment
ฉันสงสัยว่าเหตุใดคุณจึงปรับใช้ pthread_rwlock_(rd/wr)(un)lock ที่ด้านบนของ pthread_mutex_t ใหม่แทนที่จะใช้ pthread API ดั้งเดิม - person Greg Rogers; 28.10.2008
comment
เพราะฉันสามารถ / เพื่อความสนุกสนาน โปรเจ็กต์นี้เป็นเพียงการทดสอบเล็กน้อยสำหรับสิ่งอำนวยความสะดวกในการล็อคต่างๆ ของฉัน แถมยังมีประโยชน์เป็นการสาธิตด้วยตอนนี้ใช่ไหม? - person ephemient; 28.10.2008
comment
น่าเสียดายที่ไม่ใช่การใช้งานที่ใช้งานได้ Spin ที่อาจยุ่งวุ่นวายรออยู่ที่นี่: ในขณะที่ (self-›นักเขียน || self-›write_waiters); นอกจากนี้คุณยังให้วิธีแก้ปัญหา C กับคำถาม C ++ - person Martin York; 28.10.2008
comment
มันไม่หมุน: while แก้ไข do pthread_cond_wait บนบรรทัดที่อยู่ด้านบนทันที ไม่มีข้อยกเว้นในภาษา C (จริงอยู่ ฉันไม่จัดการกับเงื่อนไขข้อผิดพลาด แต่นั่นไม่ใช่ข้อยกเว้น) - person ephemient; 28.10.2008
comment
ในฟังก์ชัน reader_unlock คุณจะส่งสัญญาณให้ผู้เขียน แม้ว่าปัจจุบันจะมีผู้อ่านที่กำลังอ่านข้อมูลอยู่ก็ตาม เป็นการดีกว่าที่จะเพิ่มเงื่อนไขของคุณถ้า (self-›readers == 0 && self-›write_waiters) ... - person Anna; 11.01.2010
comment
อย่างที่ฉันบอกไป การใช้งานนี้มีความลำเอียงเล็กน้อยต่อนักเขียน (นักเขียนจำนวนมากอาจทำให้ผู้อ่านอดอยากได้อย่างไม่มีกำหนด) เพียงแก้ไข writer_unlock หากคุณต้องการให้ความสมดุลเป็นอย่างอื่น - person ephemient; 11.01.2010
comment
ฉันกำลังพยายามสร้างประเด็นที่แตกต่างออกไปที่นี่ ถ้ามีผู้อ่านยังอ่านอยู่ก็ไม่มีประโยชน์ที่จะส่งสัญญาณให้ผู้เขียนเพราะคนเขียนจะไม่เริ่มทำงานและแค่จับล็อคและใช้เวลาเปล่าประโยชน์ นี่เป็นเรื่องของความไร้ประสิทธิภาพเล็กน้อย ไม่ใช่เรื่องสำคัญ - person Anna; 11.01.2010
comment
หมายความว่าไงไม่มีประเด็น? นักเขียนหน้าใหม่สามารถไขกุญแจได้อย่างมีความสุข แม้ว่าจะมีผู้อ่านรอล็อคอยู่ ตราบใดที่ไม่มีใครคอยล็อคอยู่จริงๆ และเริ่มทำงานต่อ หากคุณมีผู้อ่านอย่างต่อเนื่องและไม่ต้องการให้นักเขียนเรื่องสั้นถูกบล็อกเป็นครั้งคราว นี่คือความสมดุลที่ดีกว่า - person ephemient; 11.01.2010

ไม่ว่าคุณจะตัดสินใจใช้อะไรก็ตาม ให้เปรียบเทียบภาระงานของคุณกับล็อคแบบธรรมดา เนื่องจากการล็อคการอ่าน/เขียนมักจะช้ากว่า mutex แบบธรรมดา 3-40 เท่า เมื่อไม่มีการโต้แย้ง

นี่คือข้อมูลอ้างอิงบางส่วน

person ididak    schedule 28.10.2008
comment
น่าสนใจมาก เรากำลังเปรียบเทียบทุกอย่างอย่างแน่นอน - person Matt Price; 28.10.2008
comment
ลิงก์ไม่ถูกต้อง - person Behrouz.M; 31.12.2018
comment
หากต้องการอ่านเพิ่มเติมเกี่ยวกับเรื่องนี้: C++ Concurrency in Action 2nd เอ็ด โดย Anthony Williams บทที่ 3.3.2 และใหม่กว่า 8-th - person marcinj; 06.08.2019
comment
การทดสอบง่ายๆ แสดงให้เห็นว่าการล็อก r/w อาจช้าลงจริงๆ ในบางกรณี: coliru.stacked-crooked com/a/dce6e59b0f01d513 - person marcinj; 06.08.2019

แก้ไข: ลิงก์ MSDN Magazine ไม่สามารถใช้งานได้อีกต่อไป บทความ CodeProject มีอยู่ใน https://www.codeproject.com/Articles/32685/Testing-reader-writer-locks และสรุปได้ค่อนข้างดี นอกจากนี้ ฉันยังพบลิงก์ MSDN ใหม่เกี่ยวกับ Compound Synchronization Objects

มีบทความเกี่ยวกับการล็อกตัวอ่านและผู้เขียนบน MSDN ที่นำเสนอบางส่วน การใช้งานของพวกเขา นอกจากนี้ยังแนะนำ Slim reader/writer lock ซึ่งเป็นการซิงโครไนซ์เคอร์เนลแบบดั้งเดิมที่นำมาใช้กับ Vista นอกจากนี้ยังมีบทความ CodeProject เกี่ยวกับการเปรียบเทียบการใช้งานที่แตกต่างกัน (รวมถึงบทความ MSDN ด้วย) .

person vividos    schedule 29.01.2009
comment
ประสบการณ์ของฉันกับ slim rw lock คือมันเร็วมากเมื่อเทียบกับแบบที่ใช้ mutexes และสัญญาณ - person Laserallan; 02.03.2009
comment
สิ่งที่น่ารำคาญคือในการใช้งานคุณต้องเขียนการตรวจจับระบบปฏิบัติการรันไทม์และใช้พอยน์เตอร์ฟังก์ชันหรือคลาสเสมือนเพื่อเลือกกลยุทธ์การล็อค ยังไม่เหมาะสมที่จะเพิกเฉยต่อ XP - person Zan Lynx; 31.08.2010
comment
ใช่คุณพูดถูกแซน แค่อยากเล่าเผื่อมีคนเจอคำถามแล้วนำไปใช้ได้ - person vividos; 31.08.2010
comment
@vividos: คุณมีลิงก์ MSDN ที่ใช้งานไม่ได้ - person ahmd0; 17.07.2017

C++17 รองรับ std::shared_mutex รองรับใน MSVC++ 2015 และ 2017 .

person Serge Rogatch    schedule 28.05.2017
comment
และใน C++14 จะมี shared_timed_mutex และ shared_lock. - person Jan Kundrát; 22.10.2018

Intel Thread Building Blocks ยังมี rw_lock ให้เลือกสองสามแบบ:

http://www.threadingbuildingblocks.org/

พวกมันมี spin_rw_mutex สำหรับช่วงการโต้แย้งที่สั้นมาก และ Queuing_rw_mutex สำหรับช่วงการโต้แย้งที่นานกว่า แบบแรกสามารถนำมาใช้ในโค้ดที่คำนึงถึงประสิทธิภาพโดยเฉพาะ อย่างหลังนั้นเทียบเคียงได้ดีกว่าในด้านประสิทธิภาพกับที่ได้รับจาก Boost.Thread หรือใช้ pthreads โดยตรง แต่โปรไฟล์เพื่อให้แน่ใจว่าอันไหนชนะสำหรับรูปแบบการเข้าถึงของคุณ

person Edward KMETT    schedule 05.11.2008
comment
เอ่อ ไม่ใช่ว่าฉันจำเป็นต้องแนะนำให้นำ TBB ทั้งหมดเข้ามาเพื่อรับ rw_locks - person Edward KMETT; 07.11.2008
comment
แทนที่จะเลือกตัวแปรตามโค้ดที่ละเอียดอ่อนต่อประสิทธิภาพโดยเฉพาะ ตัวเลือกควรขึ้นอยู่กับระยะเวลาเฉลี่ยที่จะระงับการล็อค หากล็อคไว้เพียงเพื่ออ่านจำนวนเต็มสองสามจำนวนแล้วปล่อยหลังจากนั้น การหมุนจะเหมาะสม มิฉะนั้นการเข้าคิวจะเหมาะสม - person rwong; 27.02.2013
comment
@rwong ฉันเห็นด้วยอย่างยิ่ง ฉันตั้งใจที่จะสื่อถึงกรอบเวลาที่เหมาะสมในประโยคที่แล้ว และอาจจะทำให้ฟังดูไร้สาระมากกว่าที่เป็นอยู่ ;) - person Edward KMETT; 28.02.2013

Boost.Thread มีตั้งแต่เปิดตัว 1.35.0 แล้ว รองรับการล็อคผู้อ่านและนักเขียน สิ่งที่ดีเกี่ยวกับเรื่องนี้ก็คือ การใช้งานเป็นแบบข้ามแพลตฟอร์มอย่างมาก มีการตรวจสอบโดยผู้ทรงคุณวุฒิ และจริงๆ แล้ว การใช้งานอ้างอิงสำหรับมาตรฐาน C++0x ที่กำลังจะมาถึง

person Dean Michael    schedule 28.10.2008
comment
ใช่แล้ว - แต่ Boost.Thread ที่เป็นพื้นฐานสำหรับเธรด C ++ 0x ไม่ได้เป็นส่วนหนึ่งของคำตอบที่ยอมรับข้างต้น พิจารณาข้อมูลเพิ่มเติมนี้ :) - person Dean Michael; 29.10.2008

ฉันขอแนะนำ ไลบรารี ACE ซึ่งมีกลไกการล็อกมากมายและ ถูกพอร์ตไปยังแพลตฟอร์มต่างๆ

ขึ้นอยู่กับเงื่อนไขขอบเขตของปัญหาของคุณ คุณอาจพบว่าคลาสต่อไปนี้มีประโยชน์:

  • ACE_RW_Process_Mutex
  • ACE_Write_Guard และ ACE_Read_Guard
  • ACE_Condition
person Dong Hoon    schedule 28.10.2008
comment
ฉันไม่รังเกียจ แต่ฉันไม่คิดว่าจะสามารถนำไลบรารี ACE มาสู่งานสร้างของเราได้ในตอนนี้ ฉันเคยใช้มันในอดีตและพวกเขาก็เยี่ยมมาก - person Matt Price; 28.10.2008
comment
อะไรก็ตามที่เกี่ยวข้องกับ ACE นั้นไม่ดี มันเป็นฐานรหัสโบราณ มีการจัดทำเอกสารไม่ดีและไม่สามารถเข้าไปได้ หลีกเลี่ยงการทำให้โค้ดของคุณติดไวรัสไม่ว่าจะด้วยวิธีใดก็ตาม - person shoosh; 28.07.2011
comment
ฉันเห็นด้วยอย่างยิ่งกับ @shoosh: แม้แต่ลิงก์ที่ให้ไว้ในคำตอบ 404 :-( โครงการที่ฉันกำลังทำอยู่ (ซึ่งเริ่มในปลายศตวรรษที่ 17) ก็ใช้มัน ดังนั้นฉันจะไม่มีทางเลือกอื่นนอกจากต้องทำ แต่สำหรับโค้ดที่ค่อนข้างใหม่: อยู่ห่างจาก ACE ไม่ว่าอย่างไรก็ตาม - person ssc; 11.06.2014
comment
ในหัวข้อว่า ACE มีความร่วมสมัยเพียงใด โปรดดูสิ่งนี้และคำถาม SO นี้ - person ssc; 11.06.2014

http://www.codeproject.com/KB/threads/ReaderWriterLock.aspx

นี่คือการใช้งานที่ดีและไม่ซับซ้อนซึ่งเหมาะสำหรับงานส่วนใหญ่

person Vladislav Rastrusny    schedule 08.03.2011

คลาสล็อคการซิงโครไนซ์ตัวอ่านหลายตัวและนักเขียนเดี่ยวสำหรับ Win32 โดย Glenn Slayde

http://www.glennslayden.com/code/win32/reader-writer-lock

person KindDragon    schedule 31.05.2011

คุณสามารถคัดลอก ReentrantReadWriteLock ที่ยอดเยี่ยมของ Sun ได้. มันมีฟีเจอร์ต่างๆ เช่น ความเป็นธรรมที่เป็นตัวเลือก การดาวน์เกรดการล็อก และการกลับเข้าใหม่

ใช่ มันอยู่ใน Java แต่คุณสามารถอ่านและแปลงเป็น C++ ได้อย่างง่ายดาย แม้ว่าคุณจะไม่รู้จัก Java ก็ตาม เอกสารประกอบที่ฉันเชื่อมโยงไปนั้นมีคุณสมบัติด้านพฤติกรรมทั้งหมดของการใช้งานนี้ ดังนั้นคุณจึงมั่นใจได้ว่ามันจะเป็นไปตามที่คุณต้องการ

ถ้าไม่มีอะไรก็เป็นแนวทาง

person Jason Cohen    schedule 28.10.2008
comment
Java และ C++ มีสไตล์ที่แตกต่างกันมาก นั่นอาจไม่ใช่ความคิดที่ดี Java threading ในตัวเป็นภาษา C ++ ไม่ได้ รหัส (แปลโดยตรง) ไม่ใช่ข้อยกเว้นที่ปลอดภัย - person Martin York; 28.10.2008
comment
นั่นไม่เป็นความจริงเลย ที่จริงแล้ว กฎการจัดลำดับคำสั่งใหม่ของ Java นั้น มากกว่า อนุญาตมากกว่าคอมไพเลอร์ C++ ส่วนใหญ่ ซึ่งหมายความว่า ปลอดภัยกว่า คำกล่าวของคุณที่ว่าเธรดเป็นภาษานั้นเป็นเรื่องที่สงสัยเพราะพฤติกรรมของเธรดถูกกำหนดไว้อย่างชัดเจนว่าเป็น mutex และระบบล็อคมาตรฐาน ซึ่งระบบเธรด C++ ทั้งหมดที่ฉันเคยใช้ก็มีเช่นกัน ดังนั้นการแปลจะง่ายอย่างแน่นอน - person Jason Cohen; 20.12.2009
comment
ฉันคิดว่าคุณพลาดจุดยกเว้นด้านความปลอดภัย Java ไม่ได้ใช้ RAII ตามสำนวน และ C++ ไม่มี finally ดังนั้นการแปลโดยตรงจาก Java เป็น C++ จึงเป็นความคิดที่ไม่ดีเมื่อมีข้อยกเว้น - person Tom; 23.01.2010