ตัวอย่างสำหรับ boost shared_mutex (การอ่านหลายครั้ง / การเขียนหนึ่งครั้ง)?


116

ฉันมีแอปมัลติเธรดที่ต้องอ่านข้อมูลบ่อยครั้งและมีการอัปเดตข้อมูลเป็นครั้งคราว ตอนนี้ mutex ช่วยให้เข้าถึงข้อมูลนั้นได้อย่างปลอดภัย แต่มีราคาแพงเพราะฉันต้องการให้หลายเธรดสามารถอ่านพร้อมกันได้และล็อคเฉพาะเมื่อจำเป็นต้องอัปเดตเท่านั้น (เธรดการอัปเดตอาจรอให้เธรดอื่นเสร็จสิ้น) .

ฉันคิดว่านี่คือสิ่งที่boost::shared_mutexควรทำ แต่ฉันไม่ชัดเจนในการใช้งานและยังไม่พบตัวอย่างที่ชัดเจน

ใครมีตัวอย่างง่ายๆที่ฉันสามารถใช้เริ่มต้นได้บ้าง?


ตัวอย่างของ 1800 INFORMATION ถูกต้อง ดูเพิ่มเติมบทความนี้: มีอะไรใหม่ในหัวข้อ Boost
Assaf Lavie

อาจซ้ำกันของReader / Writer Locks ใน C ++
cHao

คำตอบ:


102

ดูเหมือนว่าคุณจะทำสิ่งนี้:

boost::shared_mutex _access;
void reader()
{
  // get shared access
  boost::shared_lock<boost::shared_mutex> lock(_access);

  // now we have shared access
}

void writer()
{
  // get upgradable access
  boost::upgrade_lock<boost::shared_mutex> lock(_access);

  // get exclusive access
  boost::upgrade_to_unique_lock<boost::shared_mutex> uniqueLock(lock);
  // now we have exclusive access
}

7
นี่เป็นครั้งแรกที่ฉันใช้บูสต์และฉันเป็นมือใหม่ C ++ ดังนั้นอาจมีบางอย่างที่ฉันขาดหายไป - แต่ในรหัสของฉันเองฉันต้องระบุประเภทดังนี้: boost :: shared_lock <shared_mutex> lock (_เข้าไป);
Ken Smith

2
ฉันพยายามใช้ตัวเอง แต่ได้รับข้อผิดพลาด ไม่มีอาร์กิวเมนต์ของเทมเพลตก่อน "ล็อก" ความคิดใด ๆ ?
Matt

2
@shaz สิ่งเหล่านี้อยู่ในขอบเขต แต่คุณสามารถปล่อยได้ก่อนเวลาด้วย. unlock () หากคุณต้องการ
mmocny

4
ฉันได้เพิ่มอาร์กิวเมนต์เทมเพลตที่ขาดหายไป

1
@raaj คุณสามารถรับ upgrade_lock ได้ แต่การอัปเกรดเป็นล็อคที่ไม่ซ้ำกันจะถูกบล็อกจนกว่า shared_lock จะถูกปล่อยออกมา
1800 ข้อมูล

166

1800 ข้อมูลถูกต้องมากหรือน้อย แต่มีปัญหาบางประการที่ฉันต้องการแก้ไข

boost::shared_mutex _access;
void reader()
{
  boost::shared_lock< boost::shared_mutex > lock(_access);
  // do work here, without anyone having exclusive access
}

void conditional_writer()
{
  boost::upgrade_lock< boost::shared_mutex > lock(_access);
  // do work here, without anyone having exclusive access

  if (something) {
    boost::upgrade_to_unique_lock< boost::shared_mutex > uniqueLock(lock);
    // do work here, but now you have exclusive access
  }

  // do more work here, without anyone having exclusive access
}

void unconditional_writer()
{
  boost::unique_lock< boost::shared_mutex > lock(_access);
  // do work here, with exclusive access
}

นอกจากนี้โปรดทราบว่าแตกต่างจาก shared_lock เพียงเธรดเดียวเท่านั้นที่สามารถรับ upgrade_lock ได้ในครั้งเดียวแม้ว่าจะไม่ได้อัปเกรดก็ตาม (ซึ่งฉันคิดว่ามันอึดอัดเมื่อเจอมัน) ดังนั้นหากผู้อ่านทั้งหมดของคุณเป็นนักเขียนที่มีเงื่อนไขคุณต้องหาวิธีแก้ปัญหาอื่น


1
เพียงแสดงความคิดเห็นเกี่ยวกับ "วิธีแก้ปัญหาอื่น" เมื่อผู้อ่านของฉันทุกคนซึ่งเป็นนักเขียนที่มีเงื่อนไขสิ่งที่ฉันทำคือให้พวกเขาได้รับ shared_lock เสมอและเมื่อฉันต้องการอัปเกรดเพื่อเขียนสิทธิพิเศษฉันจะ. unlock () ตัวอ่านล็อกและรับ unique_lock ใหม่ สิ่งนี้จะทำให้ตรรกะของแอปพลิเคชันของคุณซับซ้อนขึ้นและขณะนี้มีหน้าต่างแห่งโอกาสสำหรับนักเขียนคนอื่นในการเปลี่ยนสถานะจากตอนที่คุณอ่านครั้งแรก
mmocny

8
บรรทัดไม่ควรboost::unique_lock< boost::shared_mutex > lock(lock);อ่านboost::unique_lock< boost::shared_mutex > lock( _access ); ?
SteveWilkinson

4
ข้อแม้สุดท้ายนั้นแปลกมาก หากเธรดเดียวสามารถเก็บ upgrade_lock ได้ในแต่ละครั้งความแตกต่างระหว่าง upgrade_lock และ unique_lock คืออะไร?
Ken Smith

2
@Ken ฉันไม่ค่อยชัดเจนนัก แต่ประโยชน์ของ upgrade_lock คือมันไม่ได้ปิดกั้นหากขณะนี้มี shared_locks บางส่วนที่ได้รับ (อย่างน้อยก็จนกว่าคุณจะอัพเกรดเป็น unique) อย่างไรก็ตามเธรดที่สองเพื่อทดลองและรับ upgrade_lock จะบล็อกแม้ว่าเธรดแรกจะไม่ได้อัปเกรดเป็นเฉพาะซึ่งฉันไม่คาดคิดก็ตาม
mmocny

6
นี่เป็นปัญหาการเพิ่มที่ทราบ ดูเหมือนว่าจะได้รับการแก้ไขที่ boost 1.50 beta: svn.boost.org/trac/boost/ticket/5516
Ofek Shilon

47

ตั้งแต่ 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;

5
typedef boost::unique_lock< Lock > WriteLock; typedef boost::shared_lock< Lock > ReadLock;ฉันยังพูดว่า
เถาวัลย์

6
ไม่จำเป็นต้องรวมเธรดทั้งหมด. หากคุณต้องการเพียงแค่ล็อคให้รวมล็อค ไม่ใช่การใช้งานภายใน รวมไว้ให้น้อยที่สุด
Yochai Timmer

5
แน่นอนว่าการนำไปใช้งานที่ง่ายที่สุด แต่ฉันคิดว่ามันสับสนที่จะอ้างถึงทั้ง mutexes และ locks เป็น Locks mutex คือ mutex ตัวล็อคคือสิ่งที่ทำให้มันอยู่ในสถานะล็อค
Tim MB

17

เพื่อเพิ่มข้อมูลเชิงประจักษ์เพิ่มเติมฉันได้ตรวจสอบปัญหาทั้งหมดของการล็อกที่สามารถอัพเกรดได้และตัวอย่างสำหรับการเพิ่ม shared_mutex (การอ่านหลายครั้ง / การเขียนหนึ่งครั้ง)? เป็นคำตอบที่ดีในการเพิ่มข้อมูลสำคัญที่เธรดเดียวเท่านั้นที่สามารถมี upgrade_lock ได้แม้ว่าจะไม่ได้อัปเกรดก็ตามนั่นมีความสำคัญเนื่องจากหมายความว่าคุณไม่สามารถอัปเกรดจากการล็อกที่ใช้ร่วมกันเป็นล็อกที่ไม่ซ้ำกันได้โดยไม่ต้องปลดล็อกที่ใช้ร่วมกันก่อน (มีการพูดถึงที่อื่น แต่หัวข้อที่น่าสนใจที่สุดอยู่ที่นี่http://thread.gmane.org/gmane.comp.lib.boost.devel/214394 )

อย่างไรก็ตามฉันพบความแตกต่างที่สำคัญ (ไม่มีเอกสาร) ระหว่างเธรดที่รอการอัปเกรดเป็นล็อก (กล่าวคือต้องรอให้ผู้อ่านทั้งหมดคลายล็อกที่ใช้ร่วมกัน) และล็อกตัวเขียนกำลังรอสิ่งเดียวกัน (เช่น unique_lock)

  1. เธรดที่กำลังรอ unique_lock บน shared_mutex บล็อกผู้อ่านใหม่ ๆ ที่เข้ามาพวกเขาต้องรอคำขอจากนักเขียน สิ่งนี้ทำให้ผู้อ่านไม่อดอยากนักเขียน (แต่ฉันเชื่อว่านักเขียนอาจทำให้ผู้อ่านอดอยาก)

  2. เธรดที่กำลังรอ upgradeable_lock เพื่ออัปเกรดอนุญาตให้เธรดอื่นได้รับการล็อกที่ใช้ร่วมกันดังนั้นเธรดนี้อาจถูกหยุดทำงานหากผู้อ่านใช้งานบ่อยมาก

นี่เป็นประเด็นสำคัญที่ต้องพิจารณาและควรจัดทำเป็นเอกสาร


3
Terekhov algorithmเพื่อให้แน่ใจว่าใน1.ผู้เขียนไม่สามารถอดอ่าน ดูนี้ แต่2.เป็นเรื่องจริง. upgrade_lock ไม่รับประกันความเป็นธรรม ดูนี้
JonasVautherin

2

ใช้เซมาฟอร์ที่มีจำนวนเท่ากับจำนวนผู้อ่าน ให้ผู้อ่านแต่ละคนใช้เวลานับหนึ่งของเซมาฟอร์เพื่ออ่านเพื่อที่จะอ่านได้ทั้งหมดในเวลาเดียวกัน จากนั้นให้ผู้เขียนนำจำนวนสัญญาณทั้งหมดก่อนที่จะเขียน สิ่งนี้ทำให้ผู้เขียนต้องรอให้การอ่านทั้งหมดจบลงแล้วจึงปิดกั้นการอ่านขณะเขียน


(1) คุณจะทำอย่างไรให้เป็นนักเขียนพร่องนับตามจำนวนเงินที่พลอะตอม ? (2) หากผู้เขียนลดจำนวนลงเป็นศูนย์จะรอให้ผู้อ่านที่ทำงานอยู่แล้วเสร็จก่อนเขียนได้อย่างไร?
Ofek Shilon

ความคิดที่ไม่ดี: หากนักเขียนสองคนพยายามเข้าถึงพร้อมกันคุณอาจหยุดชะงักได้
Caduchon

2

จิมมอร์ริสตอบรับอย่างดีเยี่ยมฉันสะดุดกับเรื่องนี้และใช้เวลาสักพักกว่าจะคิดได้ นี่คือโค้ดง่ายๆที่แสดงว่าหลังจากส่ง "คำขอ" สำหรับการเพิ่ม unique_lock (เวอร์ชัน 1.54) จะบล็อกคำขอ shared_lock ทั้งหมด สิ่งนี้น่าสนใจมากสำหรับฉันเพราะฉันคิดว่าการเลือกระหว่าง unique_lock และ upgradeable_lock ช่วยให้ถ้าเราต้องการเขียนลำดับความสำคัญหรือไม่มีลำดับความสำคัญ

นอกจากนี้ (1) ในโพสต์ของ Jim Morris ดูเหมือนจะขัดแย้งกับสิ่งนี้: Boost shared_lock อ่านที่ต้องการ?

#include <iostream>
#include <boost/thread.hpp>

using namespace std;

typedef boost::shared_mutex Lock;
typedef boost::unique_lock< Lock > UniqueLock;
typedef boost::shared_lock< Lock > SharedLock;

Lock tempLock;

void main2() {
    cout << "10" << endl;
    UniqueLock lock2(tempLock); // (2) queue for a unique lock
    cout << "11" << endl;
    boost::this_thread::sleep(boost::posix_time::seconds(1));
    lock2.unlock();
}

void main() {
    cout << "1" << endl;
    SharedLock lock1(tempLock); // (1) aquire a shared lock
    cout << "2" << endl;
    boost::thread tempThread(main2);
    cout << "3" << endl;
    boost::this_thread::sleep(boost::posix_time::seconds(3));
    cout << "4" << endl;
    SharedLock lock3(tempLock); // (3) try getting antoher shared lock, deadlock here
    cout << "5" << endl;
    lock1.unlock();
    lock3.unlock();
}

จริงๆแล้วฉันมีปัญหาในการหาสาเหตุที่โค้ดด้านบนหยุดชะงักในขณะที่โค้ดใน [ stackoverflow.com/questions/12082405/…ใช้งานได้
dale1209

1
จริงๆแล้วการหยุดชะงักใน (2) ไม่ใช่ใน (3) เนื่องจาก (2) กำลังรอให้ (1) คลายล็อก ข้อควรจำ: ในการรับล็อกที่ไม่ซ้ำกันคุณต้องรอให้การล็อกที่ใช้ร่วมกันทั้งหมดเสร็จสิ้น
JonasVautherin

@JonesV แม้ว่า (2) จะรอให้การล็อกที่แชร์ทั้งหมดเสร็จสิ้น แต่ก็จะไม่เป็นการชะงักงันเพราะเป็นเธรดที่แตกต่างจากเธรดที่ได้รับ (1) หากไม่มีบรรทัด (3) โปรแกรมจะ จบโดยไม่มีการหยุดชะงัก
SagiLow
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.