最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++使用Mutex實(shí)現(xiàn)讀寫(xiě)鎖思路

 更新時(shí)間:2025年05月30日 08:54:52   作者:Afeather  
這篇文章主要介紹了C++使用Mutex實(shí)現(xiàn)讀寫(xiě)鎖思路,詳細(xì)介紹了讀寫(xiě)鎖需要具有的特征,通過(guò)實(shí)例代碼給大家講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧

近期答辯完成了,想回頭看看之前沒(méi)做過(guò)的2PL。

實(shí)現(xiàn)2PL有4種方式:

  • 死鎖檢測(cè)。本篇是為了做這個(gè)而實(shí)現(xiàn)的,做這個(gè)事情的原因是c++標(biāo)準(zhǔn)庫(kù)的shared_mutex無(wú)法從外界告知獲取鎖失敗。
  • 如果需要等待,那么馬上結(jié)束txn。C++中有try_lock這樣的方式,如果上鎖失敗就返回false,這樣就可以實(shí)現(xiàn)這個(gè)了。
  • 如果需要等待,那么殺死當(dāng)前已經(jīng)獲得鎖的一方。
  • 在上鎖前對(duì)資源排序。

2和4是最簡(jiǎn)單的,沒(méi)什么好說(shuō)的。3比1略容易一些。

基本思路

一個(gè)讀寫(xiě)鎖應(yīng)該具有以下特征:

  • 多個(gè)讀者可以同時(shí)訪問(wèn)
  • 寫(xiě)者獨(dú)占訪問(wèn)
  • 寫(xiě)者與讀者互斥
  • 避免寫(xiě)者饑餓或讀者饑餓
  • 鎖的遞歸使用

由于實(shí)現(xiàn)的鎖不能夠出現(xiàn)讀餓死、寫(xiě)?zhàn)I死的現(xiàn)象,所以我想到一個(gè)很簡(jiǎn)單的方法:先到先得。當(dāng)然也許會(huì)有其他方案。

先到先得的方式下,如何判斷一個(gè)線程是否該阻塞?

  • 第一個(gè)寫(xiě)請(qǐng)求之前的所有讀請(qǐng)求可以進(jìn)行
  • 如果第一個(gè)請(qǐng)求是寫(xiě)請(qǐng)求,那么只有這一個(gè)寫(xiě)請(qǐng)求可以進(jìn)行
  • 如果沒(méi)有寫(xiě)請(qǐng)求,那么所有讀都可以進(jìn)行
  • 如果沒(méi)有讀請(qǐng)求,那么第一個(gè)寫(xiě)請(qǐng)求可以進(jìn)行。這實(shí)際是2的特殊情況
  • 其他請(qǐng)求都不可以進(jìn)行

我們畫(huà)圖來(lái)說(shuō)明一下。

假定某一刻有這些請(qǐng)求被阻塞,現(xiàn)在考慮挑出來(lái)可以執(zhí)行的線程來(lái)執(zhí)行

隊(duì)列中,第一個(gè)寫(xiě)請(qǐng)求之前的讀都可以進(jìn)行,所以此時(shí)1,2線程是可以執(zhí)行的。它們讀完后釋放鎖,于是在這個(gè)隊(duì)列中刪除了1,2

1,2刪除后,3可以正常執(zhí)行。6在環(huán)檢測(cè)的時(shí)候被要求結(jié)束,然后線程3也結(jié)束了,所以此時(shí)所有的讀都可以進(jìn)行。

實(shí)現(xiàn)先到先得,可以通過(guò)記錄正在進(jìn)行讀的線程數(shù)量,正在進(jìn)行寫(xiě)的線程數(shù)量,請(qǐng)求寫(xiě)但是被阻塞的線程數(shù)量,請(qǐng)求讀但是被阻塞的線程數(shù)量,然后根據(jù)條件來(lái)分配資源給某個(gè)線程……維護(hù)的信息數(shù)量可能不止這些,比如說(shuō)需要維護(hù)哪些線程的讀被阻塞了。

而環(huán)檢測(cè)的2PL,我們需要在外界通知線程鎖獲取失敗,所以選擇了使用隊(duì)列來(lái)實(shí)現(xiàn),這個(gè)隊(duì)列需要支持:

  • 添加讀者、寫(xiě)者(AddReader, AddWriter)
  • 刪除讀者、寫(xiě)者(RemoveReader, RemoveWriter,為了簡(jiǎn)化,統(tǒng)一為一個(gè)Remove了)
  • 當(dāng)可以獲得鎖的時(shí)候,提醒可以獲得鎖的線程。這個(gè)可以用condition_variable實(shí)現(xiàn)
  • 確定某個(gè)線程是否應(yīng)該阻塞

然而做這樣一個(gè)隊(duì)列還是需要費(fèi)一些功夫的。

隊(duì)列實(shí)現(xiàn)

明確了功能需求后,考慮一下需要什么樣的數(shù)據(jù)結(jié)構(gòu)。普通的隊(duì)列肯定是不夠的,畢竟我們會(huì)刪除其中任意一個(gè)元素,容易想到的是map/set。然后考慮到先到先得的順序要求,可以考慮額外記錄一個(gè)邏輯時(shí)間timestamp,每當(dāng)一個(gè)請(qǐng)求到達(dá),就遞增timestamp。由于加入了timestamp,所以為了支持刪除,至少需要tid:timestamp的映射。而為了支持按timestamp查詢,至少需要timestamp:tid的映射。此外,需要記錄一個(gè)請(qǐng)求是讀還是寫(xiě),所以一共需要tid:timestamp的映射和timestamp:<tid,讀寫(xiě)標(biāo)記>的映射。

timestamp:<tid,讀寫(xiě)標(biāo)記>映射關(guān)系,很容易想到通過(guò)std::map這種天然自帶排序的數(shù)據(jù)結(jié)構(gòu)來(lái)實(shí)現(xiàn),即:

  • 從最小到最大遍歷開(kāi)頭的讀請(qǐng)求,這部分線程可以直接執(zhí)行。
  • 如果是寫(xiě)請(qǐng)求開(kāi)頭的,那么這個(gè)寫(xiě)可以直接執(zhí)行。
  • 解鎖的時(shí)候刪除該線程的記錄。

筆者在此前做了CMU15445,里面的GC的watermark和這個(gè)非常顯相似。CMU15445中作者提到了可以使用unordered_map來(lái)將時(shí)間復(fù)雜度從O(logn)優(yōu)化到O(1),這種做法我想到了,所以這里的隊(duì)列使用的都是unordered_map。

#pragma once
#ifndef READER_WRITER_QUEUE_H
#define READER_WRITER_QUEUE_H
// INSPIRED BY CMU15445 fall2023 watermark
#include <cassert>
#include <unordered_map>
class ReaderWriterQueue {
 public:
  void AddReader(int tid) {
    assert(tid_ts.count(tid) == 0);
    ts_tt[next_timestamp] = {tid, TidType::kRead};
    tid_ts[tid] = next_timestamp;
    next_timestamp++;
  }
  void AddWriter(int tid) {
    assert(tid_ts.count(tid) == 0);
    ts_tt[next_timestamp] = {tid, TidType::kWrite};
    tid_ts[tid] = next_timestamp;
    next_timestamp++;
  }
  void Remove(int tid) {
    auto ts = tid_ts.find(tid);
    if (ts == tid_ts.end()) return;
    assert(ts_tt.count(ts->second) == 1);
    ts_tt.erase(ts->second);
    tid_ts.erase(ts);
  }
  bool ShallBlock(int tid) {
    ResetMinWriteTimestamp();
    ResetMinTimestamp(); // 這兩個(gè)timestamp處理可以合并
    auto iter = tid_ts.find(tid);
    assert(iter != tid_ts.end());
    assert(ts_tt.count(iter->second) == 1);
    auto ts = iter->second;
    auto [_, type] = ts_tt[ts];
    // 如果讀者之前有寫(xiě)者,那么就需要阻塞等待
    if (type == TidType::kRead) return ts > min_write_ts;
    // 如果寫(xiě)者之前有讀者,那么就需要阻塞等待
    if (min_ts < min_write_ts) return true;
    // 如果寫(xiě)者之前有寫(xiě)者,那么就需要阻塞等待
    return ts_tt[min_write_ts].tid != tid;
  }
 private:
  void ResetMinWriteTimestamp() {
    for (; min_write_ts < next_timestamp; min_write_ts++) {
      auto iter = ts_tt.find(min_write_ts);
      if (iter == ts_tt.end()) {
        continue;
      } else if (iter->second.type == TidType::kWrite) {
        break;
      } else { // iter->second.type == TidType::kRead
        continue;
      }
    }
  }
  void ResetMinTimestamp() {
    for (; min_ts < next_timestamp; min_ts++) {
      auto iter = ts_tt.find(min_ts);
      if (iter != ts_tt.end())
        break;
    }
  }
  long next_timestamp = 0;
  long min_write_ts = 0;
  long min_ts = 0;
  struct TidType {
    int tid;
    enum LockType {kRead, kWrite} type;
    bool operator==(const TidType &rhs) const {
      return tid == rhs.tid && type == rhs.type;
    }
  };
  std::unordered_map<long, TidType> ts_tt;
  std::unordered_map<int, long> tid_ts;
};
#endif // READER_WRITER_QUEUE_H

將隊(duì)列封裝為讀寫(xiě)鎖

這一步封裝已經(jīng)非常容易了,一個(gè)請(qǐng)求到來(lái),添加到隊(duì)列中。如果需要阻塞,那么就通過(guò)condition_variable等待通知。解鎖的時(shí)候,不僅僅需要在隊(duì)列中進(jìn)行移除,還需要notify_all。notify_all還可以優(yōu)化,但是這不是那么容易的事情了,不考慮。

#pragma once
#ifndef SIMPLE_SHARED_MUTEX_H
#define SIMPLE_SHARED_MUTEX_H
#include <condition_variable>
#include <ctime>
#include <cstdio>
#include <mutex>
#include <unistd.h>
#include "reader_writer_queue.h"
class SimpleSharedMutex {
 public:
  void lock() {
    std::unique_lock lock{mtx};
    auto tid = ::gettid();
    queue.AddWriter(tid);
    while (queue.ShallBlock(tid)) cv.wait(lock);
    // printf("lock %d\n", tid);
  }
  void shared_lock() {
    std::unique_lock lock{mtx};
    auto tid = ::gettid();
    queue.AddReader(tid);
    while (queue.ShallBlock(tid)) cv.wait(lock);
    // printf("slock %d\n", tid);
  }
  void unlock() {
    std::unique_lock lock{mtx};
    queue.Remove(::gettid());
    cv.notify_all();
    // printf("ulock %d\n", ::gettid());
  }
  void shared_unlock() {
    std::unique_lock lock{mtx};
    queue.Remove(::gettid());
    cv.notify_all();
    // printf("uslock %d\n", ::gettid());
  }
 private:
  std::mutex mtx;
  ReaderWriterQueue queue;
  std::condition_variable cv;
};
#endif // SIMPLE_SHARED_MUTEX_H

到此這篇關(guān)于C++用Mutex實(shí)現(xiàn)讀寫(xiě)鎖的文章就介紹到這了,更多相關(guān)C++ Mutex讀寫(xiě)鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C中的open(),?write(),?close(),?fopen()詳解

    C中的open(),?write(),?close(),?fopen()詳解

    本文主要介紹了C語(yǔ)言中的open(),?write(),?close(),?fopen()等文件操作函數(shù),open()函數(shù)用于打開(kāi)文件,write()函數(shù)用于寫(xiě)入數(shù)據(jù),close()函數(shù)用于關(guān)閉已打開(kāi)的文件描述符
    2024-10-10
  • C語(yǔ)言仿函數(shù)(Functor)實(shí)現(xiàn)示例

    C語(yǔ)言仿函數(shù)(Functor)實(shí)現(xiàn)示例

    本文檔介紹了在C語(yǔ)言中實(shí)現(xiàn)仿函數(shù)(Functor)的方法,仿函數(shù)是一種帶狀態(tài)的可調(diào)用對(duì)象,通過(guò)結(jié)構(gòu)體和函數(shù)指針的組合模擬C++中的仿函數(shù)特性,下面就來(lái)具體介紹一下如何使用
    2026-01-01
  • C語(yǔ)言編程時(shí)常犯十八個(gè)錯(cuò)誤小結(jié)

    C語(yǔ)言編程時(shí)常犯十八個(gè)錯(cuò)誤小結(jié)

    C語(yǔ)言的最大特點(diǎn)是:功能強(qiáng)、使用方便靈活。C編譯的程序?qū)φZ(yǔ)法檢查并不象其它高級(jí)語(yǔ)言那么嚴(yán)格,這就給編程人員留下“靈活的余地”,但還是由于這個(gè)靈活給程序的調(diào)試帶來(lái)了許多不便,尤其對(duì)初學(xué)C語(yǔ)言的人來(lái)說(shuō),經(jīng)常會(huì)出一些連自己都不知道錯(cuò)在哪里的錯(cuò)誤
    2013-07-07
  • C語(yǔ)言中判斷一個(gè)char*是不是utf8編碼

    C語(yǔ)言中判斷一個(gè)char*是不是utf8編碼

    這篇文章主要介紹了C語(yǔ)言中判斷一個(gè)char*是不是utf8編碼的相關(guān)資料,需要的朋友可以參考下
    2017-06-06
  • C++入門(mén)教程之內(nèi)聯(lián)函數(shù)與extern?"C"詳解

    C++入門(mén)教程之內(nèi)聯(lián)函數(shù)與extern?"C"詳解

    C++中的內(nèi)聯(lián)函數(shù)與靜態(tài)函數(shù)靜態(tài)函數(shù)靜態(tài)函數(shù)的定義靜態(tài)函數(shù)又稱為內(nèi)部函數(shù),下面這篇文章主要給大家介紹了關(guān)于C++入門(mén)教程之內(nèi)聯(lián)函數(shù)與extern?"C"的相關(guān)資料,需要的朋友可以參考下
    2023-01-01
  • C++數(shù)據(jù)結(jié)構(gòu)繼承的概念與菱形繼承及虛擬繼承和組合

    C++數(shù)據(jù)結(jié)構(gòu)繼承的概念與菱形繼承及虛擬繼承和組合

    今天我要給大家介紹C++中更深入的內(nèi)容了。C++這門(mén)語(yǔ)言為了使代碼不冗余,做了些什么操作呢?C++的繼承就很好地實(shí)現(xiàn)了類層次的代碼復(fù)用,今天我就要來(lái)和大家好好聊一聊它了
    2022-02-02
  • C++ JSON庫(kù)nlohmann指南

    C++ JSON庫(kù)nlohmann指南

    本文詳細(xì)介紹了C++中流行的JSON庫(kù)nlohmann的功能與使用方法,包括聲明與構(gòu)造JSON對(duì)象、數(shù)組,解析與序列化JSON數(shù)據(jù),以及如何進(jìn)行元素的獲取、修改、刪除等常見(jiàn)操作,感興趣的朋友跟隨小編一起看看吧
    2025-10-10
  • C++如何刪除map容器中指定值的元素詳解

    C++如何刪除map容器中指定值的元素詳解

    map容器是C++ STL中的重要一員,刪除map容器中value為指定元素的問(wèn)題是我們經(jīng)常與遇到的一個(gè)問(wèn)題,下面這篇文章主要給大家介紹了關(guān)于利用C++如何刪除map容器中指定值的元素的相關(guān)資料,需要的朋友可以參考借鑒,下面來(lái)一起看看吧。
    2017-06-06
  • C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單學(xué)生選課管理系統(tǒng)

    C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單學(xué)生選課管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單學(xué)生選課管理系統(tǒng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-02-02
  • C 語(yǔ)言基礎(chǔ)教程(我的C之旅開(kāi)始了)[二]

    C 語(yǔ)言基礎(chǔ)教程(我的C之旅開(kāi)始了)[二]

    C 語(yǔ)言基礎(chǔ)教程(我的C之旅開(kāi)始了)[二]...
    2007-02-02

最新評(píng)論

莎车县| 崇礼县| 韶关市| 溧阳市| 横峰县| 台中市| 册亨县| 阳原县| 宣城市| 正安县| 高雄市| 东海县| 葫芦岛市| 渝中区| 杭州市| 蓝山县| 来宾市| 苍梧县| 白水县| 巴南区| 兰考县| 永嘉县| 贞丰县| 枣阳市| 新乡市| 澄城县| 涿州市| 元谋县| 五大连池市| 潜山县| 枣强县| 平邑县| 波密县| 沈丘县| 浑源县| 丰都县| 同江市| 炉霍县| 尼勒克县| 万州区| 天水市|