c++ 互斥鎖

為練習c++ 線程同步,做了LeeCode 1114題. 按序打印:

給你一個類:

public class Foo {public void first() { print("first"); }public void second() { print("second"); }public void third() { print("third"); }
}

三個不同的線程 A、B、C 將會共用一個?Foo?實例。

  • 線程 A 將會調用?first()?方法
  • 線程 B 將會調用?second()?方法
  • 線程 C 將會調用?third()?方法

請設計修改程序,以確保?second()?方法在?first()?方法之后被執行,third()?方法在?second()?方法之后被執行。

提示:

  • 盡管輸入中的數字似乎暗示了順序,但是我們并不保證線程在操作系統中的調度順序。
  • 你看到的輸入格式主要是為了確保測試的全面性。

示例 1:

輸入:nums = [1,2,3]
輸出:"firstsecondthird"
解釋:
有三個線程會被異步啟動。輸入 [1,2,3] 表示線程 A 將會調用 first() 方法,線程 B 將會調用 second() 方法,線程 C 將會調用 third() 方法。正確的輸出是 "firstsecondthird"。

示例 2:

輸入:nums = [1,3,2]
輸出:"firstsecondthird"
解釋:
輸入 [1,3,2] 表示線程 A 將會調用 first() 方法,線程 B 將會調用 third() 方法,線程 C 將會調用 second() 方法。正確的輸出是 "firstsecondthird"。

    提示:

    • nums?是?[1, 2, 3]?的一組排列

    答案&測試代碼:

    ?

    #include <iostream>
    #include "listNode.h"
    #include "solution.h"
    #include <algorithm>
    #include <unordered_set>
    #include <unordered_map>
    #include <map>
    #include <string>
    #include <stdio.h>
    #include <stdlib.h>
    #include "solution3.h"
    #include "dataDefine.h"
    #include "uthash.h"
    #include "IntArrayList.h"
    #include <string.h>
    #include <thread>
    #include <atomic>
    #include "DemoClass.h"
    #include <mutex>
    #include <condition_variable>
    #include <functional>void printFirst() {std::cout << "first";
    }void printSecond() {std::cout << "second";
    }void printThird() {std::cout << "third";
    }void testLeeCode1114_() {class Foo { // 函數內部也可以定義類。private:std::mutex mtx; // 互斥鎖std::condition_variable cv; // 條件變量bool isFirstDone;bool isSecondDone;public:Foo() {this->isFirstDone = false;this->isSecondDone = false;}void first(function<void()> printFirst) {{ // 加一對{}是為了限制下面加鎖的作用域。離開作用域lock_guard自動立即釋放鎖// lock_guard構造時立即加鎖(如果鎖被占用會等待鎖釋放,一旦鎖釋放就搶占)。不支持手動釋放鎖。lock_guard 在析構時會自動釋放鎖。std::lock_guard<std::mutex> lock(mtx); // printFirst() outputs "first". Do not change or remove this line.printFirst();this->isFirstDone = true;}cv.notify_all(); // 喚醒所有等待鎖的線程}void second(function<void()> printSecond) {// 立即加鎖,同lock_guard, 但是unique_lock比較靈活,還支持延遲加鎖。支持手動加鎖、釋放鎖。需要手動管理。std::unique_lock<std::mutex> lock(mtx);// 判斷是否滿足執行條件。如果不滿足就調用wait函數釋放鎖,該線程阻塞在這里等待被喚醒; 若滿足執行條件則繼續下面的代碼邏輯。 // 被喚醒會判斷是否滿足執行條件,且滿足條件則獲取鎖,然后繼續下面的代碼邏輯。不滿足條件則繼續等待。cv.wait(lock, [this](){return this->isFirstDone;}); // 這里的第二個參數是一個lambda表達式,表示一個匿名函數,該函數捕獲this指針(函數體中用到該指針),沒有參數。// printSecond() outputs "second". Do not change or remove this line.printSecond();this->isSecondDone = true;lock.unlock(); // 不要忘記釋放鎖。因為線程被喚醒后需要獲取鎖資源才會執行到這里,所以必須再釋放,不能因為上面wait函數釋放鎖了就不調用釋放了。cv.notify_all(); // 需要通知其他線程, 這里也可以調用notify_one, 只有一個線程在等待了。}void third(function<void()> printThird) {std::unique_lock<std::mutex> lock(mtx);cv.wait(lock, [this]() {return this->isSecondDone;});// printThird() outputs "third". Do not change or remove this line.printThird();lock.unlock();}};Foo foo; // 主線程生成Foo對象。// 因為線程要執行對象里的成員函數,所以第一個參數是函數指針, 第二個參數是該對象,后面的參數是該成員函數的傳參,這里傳遞的函數std::thread t1(&Foo::first, &foo, printFirst);std::thread t3(&Foo::third, &foo, printThird);std::thread t2(&Foo::second, &foo, printSecond);t1.join();t2.join();t3.join();std::cout << endl << "finish" << endl;
    }

    執行結果:

    ok!

    提交到LeeCode:

    ok! 沒問題。

    反面教材:

    void testLeeCode1114() { // LeeCode1114.按序打印. 反面教材,主線程加鎖,子線程解鎖,報錯:unlock of unowned mutex 。 class Foo {mutex mtx_1, mtx_2;unique_lock<mutex> lock_1, lock_2;public:Foo() : lock_1(mtx_1, try_to_lock), lock_2(mtx_2, try_to_lock) {}void first(function<void()> printFirst) {printFirst();lock_1.unlock();}void second(function<void()> printSecond) {lock_guard<mutex> guard(mtx_1);printSecond();lock_2.unlock();}void third(function<void()> printThird) {lock_guard<mutex> guard(mtx_2);printThird();}};Foo foo; // 主線程生成Foo對象。std::thread t1(&Foo::first, &foo, printFirst); // 因為是線程要執行對象里的成員函數,所以第一個參數是函數指針, 第二個參數是該對象,后面的參數是該成員函數的傳參,這里傳遞的函數std::thread t3(&Foo::third, &foo, printThird);std::thread t2(&Foo::second, &foo, printSecond);// 主線程等這3個線程執行結束:t1.join();t2.join();t3.join();// 報錯: unlock of unowned mutex 
    }

    ?報錯:unlock of unowned mutex 。?

    問題就在于主線程加鎖, 然后子線程解鎖。所以報錯。線程必須先占有鎖資源才能解鎖。

    總結: 互斥鎖就類比一個單人用的衛生間。一個人(線程)進去了會把衛生間鎖住(加鎖), 此時其他人(線程)想進去只能等待鎖釋放。

    如果一個人進去衛生間后發現不滿足辦事條件,比如沒帶紙(如示例代碼中判斷不滿足執行條件),此時出去等待(如示例代碼的wait函數釋放鎖),別人進去衛生間完事后出來通知說他用完了,然后剛才出去等待的那個人再次競爭到衛生間進去了, 然后會再次檢查條件是否滿足(如代碼中的條件判斷),發現衛生間有紙了,ok可以方便了。

    本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
    如若轉載,請注明出處:http://www.pswp.cn/bicheng/78093.shtml
    繁體地址,請注明出處:http://hk.pswp.cn/bicheng/78093.shtml
    英文地址,請注明出處:http://en.pswp.cn/bicheng/78093.shtml

    如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

    相關文章

    山東大學軟件學院創新項目實訓開發日志(20)之中醫知識問答自動生成對話標題bug修改

    在原代碼中存在一個bug&#xff1a;當前對話的標題不是現有對話的用戶的第一段的前幾個字&#xff0c;而是歷史對話的第一段的前幾個字。 這是生成標題的邏輯出了錯誤&#xff1a; 當改成size()-1即可

    WSL2-Ubuntu22.04下拉取Docker MongoDB鏡像并啟動

    若未安裝docker可參考此教程&#xff1a;可以直接在wsl上安裝docker嗎&#xff0c;而不是安裝docker desktop&#xff1f;-CSDN博客 1. 拉取鏡像 docker pull mongo:latest 2.打開網絡加速&#xff0c;再次拉取鏡像 3.創建docker-compose.yml 進入vim編輯器后輸入i進行編輯&a…

    中通 Redis 集群從 VM 遷移至 PVE:技術差異、PVE 優劣勢及應用場景深度解析

    在數字化轉型浪潮下&#xff0c;企業對服務器資源的高效利用與成本控制愈發重視。近期&#xff0c;中通快遞將服務器上的 Redis 集群服務從 VM&#xff08;VMware 虛擬化技術&#xff09;遷移至 PVE&#xff08;Proxmox VE&#xff09;&#xff0c;這一技術舉措引發了行業廣泛關…

    Prometheus+Grafana實時監控系統各項指標

    一、監控架構設計 核心組件與數據流 Prometheus&#xff1a;時序數據采集、存儲與告警規則管理Node Exporter&#xff1a;采集主機指標&#xff08;CPU、內存、磁盤、網絡等&#xff09;數據庫Exporter&#xff1a;如 mysqld_exporter、postgres_exporterGrafana&#xff1a;…

    [密碼學基礎]GMT 0029-2014簽名驗簽服務器技術規范深度解析

    GMT 0029-2014簽名驗簽服務器技術規范深度解析 引言 在數字化轉型和網絡安全需求激增的背景下&#xff0c;密碼技術成為保障數據完整性與身份認證的核心手段。中國密碼管理局發布的GMT 0029-2014《簽名驗簽服務器技術規范》&#xff0c;為簽名驗簽服務器的設計、開發與部署提…

    多路轉接select服務器

    目錄 select函數原型 select服務器 select的缺點 前面介紹過多路轉接就是能同時等待多個文件描述符&#xff0c;這篇文章介紹一下多路轉接方案中的select的使用 select函數原型 #include <sys/select.h> int select(int nfds, fd_set *readfds, fd_set *writefds, f…

    QT6 源(45):分隔條 QSplitter 允許程序的用戶修改布局,程序員使用 IDE時,就是分隔條的用戶,以及其 QSplitter 源代碼

    &#xff08;1&#xff09; &#xff08;2&#xff09;本類的繼承關系如下&#xff0c;所以說分隔條屬于容器&#xff1a; &#xff08;3&#xff09;本類的屬性&#xff1a; &#xff08;4&#xff09; 這是一份 QSplitter 的舉例代碼&#xff0c;注意其構造函數時候的傳參&am…

    VSCode PIO使用Jlink SWD燒錄Stm32

    一、背景 PIO的編譯速度比Arduino快很多&#xff0c;同樣支持Arduino的語法。VScode的自動補全和插件也能夠幫助快速開發目前使用JLINK SWD的方式連接STM32 二、配置 在ini配置文件中&#xff0c;添加如下內容 [env:genericSTM32F103C8] platform ststm32 board genericS…

    JavaScript 渲染內容爬取:Puppeteer 入門

    在現代網絡應用中&#xff0c;許多網頁內容是通過 JavaScript 渲染生成的&#xff0c;傳統的爬蟲工具往往難以獲取這些動態內容。Puppeteer 作為一種強大的瀏覽器自動化工具&#xff0c;為這一問題提供了優雅的解決方案。本文將帶你入門 Puppeteer&#xff0c;介紹如何安裝、啟…

    卷積神經網絡:視覺煉金術士的數學魔法

    引言&#xff1a;當數學遇見視覺煉金術 在人工智能的奇幻世界里&#xff0c;卷積神經網絡&#xff08;CNN&#xff09;猶如掌握視覺奧秘的煉金術士&#xff0c;將原始像素的"鉛塊"淬煉成認知的"黃金"。這種融合數學嚴謹性與生物靈感的算法架構&#xff0c…

    Android Cordova 開發 - Cordova 快速入門(Cordova 環境配置、Cordova 第一個應用程序)

    一、Cordova 1、Cordova 概述 Cordova 是使用 HTML&#xff0c;CSS 和 JavaScript 構建混合移動應用程序的平臺 2、Cordova 特征 &#xff08;1&#xff09;命令行界面&#xff08;Cordova CLI&#xff09; 這是可用于啟動項目&#xff0c;構建不同平臺的進程&#xff0c;…

    ubuntu18.04啟動不了修復

    參考: 虛擬機里的Ubuntu18.4啟動時進入到grub rescue救援模式&#xff08;無法正常進入到系統&#xff09;&#xff0c;ls查看后只有一個硬盤和分區&#xff0c;且無法找到/boot/grub文件【已解決】_ubuntu grub rescue-CSDN博客 本人fdisk錯誤使用,導致了grub啟動不了 第一步…

    SpringBoot3設置maven package直接打包成二進制可執行文件

    注意事項 SpringBoot普通native打包順序clean compile spring-boot:process-aot native:compile 使用以下配置只會的打包順序clean package&#xff08;注意&#xff1a;使用此配置以后打包會有編譯后的class文件、jar包、original源文件、二進制可執行文件【Linux是無后綴的包…

    【華為】防火墻雙擊熱備-之-主備模式-單外網線路

    FW1和FW2的業務接口都工作在三層&#xff0c;上行連接二層交換機。上行交換機連接運營商的接入點&#xff0c;運營商為企業分配的IP地址為100.100.100.2。現在希望FW1和FW2以主備備份方式工作。正常情況下&#xff0c;流量通過FW1轉發&#xff1b;當FW1出現故障時&#xff0c;流…

    MYSQL之表的操作

    1. 創建表 語法: CREATE TABLE table_name ( field1 datatype, field2 datatype, field3 datatype ) character set 字符集 collate 校驗規則 engine 存儲引擎; field 表示列名, datatype 表示列的類型character set 字符集, 如果沒有指定字符集, 則以所在數據庫的字符集為…

    RAG進階:Chroma開源的AI原生向量數據庫

    一、Chroma 核心概念與優勢 1. 什么是 Chroma&#xff1f; Chroma 是一款開源的向量數據庫&#xff0c;專為高效存儲和檢索高維向量數據設計。其核心能力在于語義相似性搜索&#xff0c;支持文本、圖像等嵌入向量的快速匹配&#xff0c;廣泛應用于大模型上下文增強&#xff0…

    店匠科技摘得 36 氪“2025 AI Partner 創新大獎”

    全場景 AI 方案驅動跨境電商數智化躍遷 4 月 18 日,36 氪 2025 AI Partner 大會于上海盛大開幕。大會緊扣“Super App 來了”主題,全力探尋 AI 時代的全新變量,探索 AI 領域下一個超級應用的無限可能性。在此次大會上,跨境電商獨立站 SaaS 平臺店匠科技(Shoplazza)憑借“店匠跨…

    SQL技術終極指南:從內核原理到超大規模應用

    一、DDL核心應用場景與最佳實踐 1.1 表結構設計場景矩陣 業務場景核心語法要素典型實現案例電商用戶畫像JSON字段虛擬列索引CREATE TABLE users (id INT, profile JSON, AS (profile->>$.age) VIRTUAL, INDEX idx_age((profile->>$.age)))物聯網時序數據分區表壓…

    吳恩達深度學習作業CNN之ResNet實現(Pytorch)

    課程中認識許多CNN架構。首先是經典網絡&#xff1a; LeNet-5AlexNetVGG 之后是近年來的一些網絡&#xff1a; ResNetInceptionMobileNet 經典網絡 LeNet-5 LeNet-5是用于手寫數字識別&#xff08;識別0~9的阿拉伯數字&#xff09;的網絡。它的結構如下&#xff1a; 網絡…

    FPGA入門學習Day1——設計一個DDS信號發生器

    目錄 一、DDS簡介 &#xff08;一&#xff09;基本原理 &#xff08;二&#xff09;主要優勢 &#xff08;三&#xff09;與傳統技術的對比 二、FPGA存儲器 &#xff08;一&#xff09;ROM波形存儲器 &#xff08;二&#xff09;RAM隨機存取存儲器 &#xff08;三&…