c++互斥鎖,競爭狀態與臨界區

競爭狀態與臨界區

  • 1,基本互斥鎖
  • 2,try_lock
  • 3,互斥鎖存在的坑—線程搶占不到資源
  • 4,超時鎖
  • 5,遞歸鎖(在一個線程內可以多次lock的鎖)recursive_mutex和recursive_timed_mutex用于業務組合
  • 6,利用棧特性自動釋放鎖RALL
    • 6.1手動實現簡易RALL
    • 6.2 c++11支持的RALL管理互斥資源 lock_guard
    • 6.3 c++11 unique_lock c++11

1,基本互斥鎖

//互斥鎖使用
#include<mutex>
static  mutex  mux;
mux.lock();
mux.unlock();

案例

// An highlighted block
#include<iostream>
#include<thread>
#include<string>
#include<windows.h>
#include<chrono>
#include<mutex>
using namespace std;
static  mutex  mux;void  TestThread1()
{mux.lock();cout << "=====================================" << endl;cout << "test 001" << endl;cout << "test 002" << endl;cout << "test 003" << endl;cout << "=====================================" << endl;mux.unlock();std::this_thread::sleep_for(std::chrono::milliseconds(50));
}void deal1()
{thread  th(TestThread1);th.join();
}int  main()
{deal1();
}

運行結果
在這里插入圖片描述
互斥鎖的作用在于可以盡可能的避免數據競爭,在lockunlock之間的區域被稱之為臨界區,要注意一點的是,臨界區要盡量小,鎖要盡早的申請且盡早的釋放

2,try_lock

if (!mux.try_lock()){}
//嘗試去鎖,成功返回true,失敗返回false

案例:

#include<windows.h>
#include<chrono>
#include<mutex>
using namespace std;
static  mutex  mux;void  TestThread1()
{for (;;){if (!mux.try_lock()){cout << "." << "flush" << endl;;this_thread::sleep_for(std::chrono::milliseconds(50));continue;}//mux.lock();cout << "=====================================" << endl;cout << "test 001" << endl;cout << "test 002" << endl;cout << "test 003" << endl;cout << "=====================================" << endl;mux.unlock();std::this_thread::sleep_for(std::chrono::milliseconds(50));}}void  deal1()
{for (int i = 0;i<10;i++){thread  th1(TestThread1);th1.detach();}getchar();
}
int  main()
{deal1();
}

注意,try_lock有性能開銷。

3,互斥鎖存在的坑—線程搶占不到資源

在2節中,unlock之后選擇讓線程睡眠50毫秒,為什么要要sleep 50毫秒呢???這就涉及到多線程并發時因多線程競爭cpu資源而容易出現的坑。

線程有這樣幾種狀態,
1,初始化(Init): 表示該線程正在創建。
2,就緒(Ready):表示該線程在就緒列表中,等待CPU調度。
3,運行(Running): 表示該線程正在運行。
4,阻塞(Blocked):該線程被阻塞掛起,包括,鎖,事件,信號量阻塞。此狀態不占用cpu資源
5,退出:該線程結束,等待父線程回收資源。

在這里插入圖片描述
一個線程在創建,就緒之后,肯直接進入阻塞狀態,也可能先進入運行狀態,后進入阻塞狀態。當由運行狀態進入阻塞狀態之后,線程在阻塞一段時間之后可能因為等待互斥鎖或條件變量,重新進入就緒態,等待cpu調度之后,才再次進入運行狀態。

實際上,多線程并發運行,在某一個時間段之內其實只有一個線程在占用cpu資源,當某一個線程調用unlock之后,所有線程會再次去爭奪cpu資源,此時會出現一個神奇的現象,那就是,某一個線程在unlock之后,cpu資源被釋放,然后多個線程再次競爭cpu資源的時候,上一個占用cpu資源的線程再次獲取到了cpu資源,導致某一個線程長期能搶占到cpu資源而其他線程就無法順利運行下去,這種情況是我們在實際的項目當中不想遇到的,于是乎,在調用unlock之后,讓現場 sleep 一段事件,這樣就可以讓其他線程爭奪cpu資源,就不會出現某一個線程長期占用cpu資源的場景了。

4,超時鎖

static timed_mutex  tmux;
if (!!tmux.try_lock_for(chrono::milliseconds(500))){}
//嘗試獲取鎖,當五百毫秒內沒有獲取到鎖資源,就會返回false

案例:

#include<iostream>
#include<thread>
#include<string>
#include<windows.h>
#include<chrono>
#include<mutex>
using namespace std;
static timed_mutex  tmux;void  ThewadMain(int i)
{for (;;){if (!tmux.try_lock_for(chrono::milliseconds(500))){cout << i << "  lock failed " << endl;continue;}else{cout << i << " [in]" << endl;tmux.unlock();std::this_thread::sleep_for(chrono::microseconds(2000));}}
}void  deal1()
{for (int i = 0; i < 3; i++){thread th(ThewadMain,i);th.detach();}
}int  main()
{deal1();getchar();
}

運行結果:
在這里插入圖片描述

5,遞歸鎖(在一個線程內可以多次lock的鎖)recursive_mutex和recursive_timed_mutex用于業務組合

在我們開發某個項目的時候,隨著時間的推移,項目代碼越來越多,接口越來越多,會出現大量的接口層層調用的情況,肯會出現的一個情況是,某個外部接口已經對某個鎖進行了 lock 但是調用的兩一個接口也對某一個鎖進行了lock ,同一個鎖在同一個線程內連續調用兩次lock會發生什么現象呢?

// An highlighted block
#include<iostream>
#include<thread>
#include<string>
#include<windows.h>
#include<chrono>
#include<mutex>
using namespace std;
static mutex  mux;void Task1()
{mux.lock();cout << "task1" << endl;mux.unlock();
}void  Task2()
{mux.lock();cout << "task2" << endl;mux.unlock();
}void  ThewadMain(int i)
{mux.lock();Task1();Task2();mux.unlock();
}void  deal1()
{thread th1(ThewadMain,1);
}int  main()
{deal1();getchar();
}

在這里插入圖片描述
我們發現,程序直接崩潰無法運行。
為了解決這個問題我們引入了遞歸鎖,所謂的遞歸鎖就是可以在一個線程中鎖多次。

static recursive_mutex  rmux;
#include<iostream>
#include<thread>
#include<string>
#include<windows.h>
#include<chrono>
#include<mutex>using namespace std;
static recursive_mutex  rmux;void Task1()
{rmux.lock();cout << "task1" << endl;rmux.unlock();
}void  Task2()
{rmux.lock();cout << "task2" << endl;rmux.unlock();
}void  ThewadMain(int i)
{rmux.lock();Task1();Task2();rmux.unlock();
}void  deal1()
{thread th1(ThewadMain,1);th1.detach();
}int  main()
{deal1();getchar();
}

在這里插入圖片描述
遞歸鎖可以多次lock,但是也要相應的進行多次unlock,比如我lock兩次,那么我也要相應的unlock兩次。

6,利用棧特性自動釋放鎖RALL

RALL c++之父提出,使用局部對象來管理資源的技術成為資源獲取即初始化,它的生命周期是由操作系統來管理的,無需人工介入;資源的銷毀容易忘記,造成死鎖或內存泄露。

簡單來說就是,我們自己管理鎖需要手動 lock 或者 手動 unlock ,但是隨著項目周期的拉長我們很可能忘記 unlock 造成死鎖,此時我們用一種方法可以不用手動實現 lock unlock 即可實現自動釋放,盡可能的避免死鎖或內存泄露。

6.1手動實現簡易RALL

#include<iostream>
#include<thread>
#include<string>
#include<windows.h>
#include<chrono>
#include<mutex>using namespace std;
static mutex  rmux;class  XMutex
{
public:XMutex(mutex& mux):mux_(mux){mux_.lock();}~XMutex(){mux_.unlock();}
private:mutex& mux_;
};void  ThreadMain(int i)
{XMutex  lock(rmux);cout << i<<" in thread" << endl;
}void  deal1()
{for (int i = 0; i < 10; i++){thread th(ThreadMain,i);th.detach();}
}int  main()
{deal1();getchar();
}

在這里插入圖片描述

6.2 c++11支持的RALL管理互斥資源 lock_guard

// A code block
static mutex  rmux;
lock_guard<mutex> lock(rmux);
#include<iostream>
#include<thread>
#include<string>
#include<windows.h>
#include<chrono>
#include<mutex>
using namespace std;
static mutex  rmux;void  ThreadMain(int i)
{{lock_guard<mutex> lock(rmux);cout << i << " in thread" << endl;}std::this_thread::sleep_for(chrono::microseconds(200));}void  deal1()
{for (int i = 0; i < 10; i++){thread th(ThreadMain,i);th.detach();}
}int  main()
{deal1();getchar();
}

如上述代碼所示,當出了大括號的范圍,自動調用析構函數,會自動進行unlock,。

6.3 c++11 unique_lock c++11

上一個小結的 lock_guard 是一個最基本的簡單的 RALL 鎖,但實際的項目開發過程會出現更復雜的業務需求,比如會存在 一個鎖賦值給另一個鎖,一個鎖移動到另一個鎖,或者可能會出現臨時手動解鎖加鎖,或者其他更復雜的需求,此時我們就會使用 unique_lock 。

// A code block
unique_lock  c++11  
支持臨時釋放鎖 unlock
支持 adopt_lock (已經擁有鎖,不加鎖,出棧區會釋放)
支持 defer_lock (延后擁有,不加鎖,出棧區不釋放)
支持 try_to_lock 嘗試獲得互斥的所有權而不阻塞,獲取失敗推出棧區不會釋放,通過 owns_lock() 函數判斷。
// An highlighted block
#include<iostream>
#include<thread>
#include<string>
#include<windows.h>
#include<chrono>
#include<mutex>
using namespace std;
static mutex  rmux;void  ThreadMain(int i)
{{unique_lock<mutex> lock(rmux);cout << i << " in thread" << endl;}std::this_thread::sleep_for(chrono::microseconds(200));}void  deal1()
{for (int i = 0; i < 10; i++){thread th(ThreadMain,i);th.detach();}
}int  main()
{deal1();getchar();
}

支持臨時釋放鎖

// An highlighted block
void  ThreadMain(int i)
{{unique_lock<mutex> lock(rmux);lock.unlock();cout << i << " in thread" << endl;lock.lock();}std::this_thread::sleep_for(chrono::microseconds(200));
}

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

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

相關文章

實戰項目:基于控制臺與數據庫的圖書管理系統開發指南

一、項目概述與設計思路 1.1 為什么選擇圖書管理系統 圖書管理系統是學習編程的經典項目&#xff0c;它涵蓋了&#xff1a; 控制臺交互&#xff1a;學習用戶輸入輸出處理 數據庫操作&#xff1a;掌握CRUD核心功能 業務邏輯&#xff1a;理解實際應用場景 系統架構&#xff…

人工智能——層次聚類算法

目錄 摘要 18 層次聚類 18.1 本章工作任務 18.2 本章技能目標 18.3 本章簡介 18.4 編程實戰 18.5 本章總結 18.6 本章作業 本章已完結&#xff01;&#xff01;&#xff01; 摘要 本章實現的工作是&#xff1a;首先導入20名學生的3科成績&#xff0c;然后根據優先聚…

Linux中安裝mysql8,轉載及注意事項

一、先前往官網下載mysql8 下載地址&#xff1a; https://dev.mysql.com/downloads/選擇Linux 二、刪除Linux中的mysql&#xff08;如果有的話&#xff09;&#xff0c;上傳安裝包 1、先查看mysql是否存在&#xff0c;命令如下&#xff1a; rpm -qa|grep -i mysql如果使用這…

《算法導論(第4版)》閱讀筆記:p4-p5

《算法導論(第4版)》學習第 3 天&#xff0c;p4-p5 總結&#xff0c;總計 2 頁。 一、技術總結 1.instance Thus, given the input sequence h31; 41; 59; 26; 41; 58i, a correct sorting algorithm returns as output the sequence h26; 31; 41; 41; 58; 59i. Such an inp…

第十四篇:系統分析師第三遍——15章

目錄 一、目標二、計劃三、完成情況四、意外之喜(最少2點)1.計劃內的明確認知和思想的提升標志2.計劃外的具體事情提升內容和標志 五、總結六、后面準備怎么做&#xff1f; 一、目標 通過參加考試&#xff0c;訓練學習能力&#xff0c;而非單純以拿證為目的。 1.在復習過程中&…

Easy云盤總結篇-登錄注冊

**說在前面&#xff1a;該項目是跟著B站一位大佬寫的&#xff0c;不分享源碼&#xff0c;支持項目付費 ** 獲取圖形驗證碼 可以看到這里有2兩種圖形驗證碼&#xff0c;分為&#xff1a; type0&#xff1a;如上圖下面那個&#xff0c;是完成操作后要進行注冊的驗證碼 type1: 如…

【前端知識】Vue3狀態組件Pinia詳細介紹

Vue3狀態組件Pinia詳細介紹 關聯知識 Pinia 組件介紹、核心原理及使用方式 Pinia 組件介紹 Pinia 是 Vue.js 的官方狀態管理庫&#xff0c;專為 Vue 3 設計&#xff0c;提供簡潔的 API 和強大的 TypeScript 支持。其核心組件包括&#xff1a; ? Store&#xff1a;狀態存儲容器…

mysql 云服務遠程linux創建數據庫

1. 本地使用已創建好的用戶創建數據庫出現問題 提示access deniey finalshell遠程創建新用戶 :~# mysql -u root -pR***34 > CREATE DATABASE r***e; > CREATE USER r**ue% IDENTIFIED BY Ry****34; > GRANT ALL PRIVILEGES ON ry_vue.* TO r***e%; > FLUSH PRI…

【“星瑞” O6 評測】 — CPU llama.cpp不同優化速度對比

前言 隨著大模型應用場景的不斷拓展&#xff0c;arm cpu 憑借其獨特優勢在大模型推理領域的重要性日益凸顯。它在性能、功耗、架構適配等多方面發揮關鍵作用&#xff0c;推動大模型在不同場景落地 1. Kleidi AI 簡介 Arm Kleidi 成為解決這些挑戰的理想方案&#xff0c;它能…

wireshark抓包也能被篡改?

wireshark本身并不能修改數據包&#xff0c;但是tcprewrite 可以修改數據包&#xff0c;然后通過tcpreplay 進行重放&#xff0c;這個時候wireshark抓的包&#xff0c;就是被篡改后的pcap包了。 ailx10 網絡安全優秀回答者 互聯網行業 安全攻防員 去咨詢 步驟一&#xff1a…

使用PyTorch進行熱狗圖像分類模型微調

本教程將演示如何使用PyTorch框架對預訓練模型進行微調&#xff0c;實現熱狗與非熱狗圖像的分類任務。我們將從數據準備開始&#xff0c;逐步完成數據加載、可視化等關鍵步驟。 1. 環境配置與庫導入 %matplotlib inline import os import torch from torch import nn from d2l…

內容中臺與企業內容管理核心差異剖析

功能定位與架構設計差異 在企業數字化進程中&#xff0c;內容中臺與企業內容管理&#xff08;ECM&#xff09;的核心差異首先體現在功能定位層面。傳統ECM系統以文檔存儲、版本控制及權限管理為核心&#xff0c;主要服務于企業內部知識庫的靜態管理需求&#xff0c;例如通過Ba…

使用PyMongo連接MongoDB的基本操作

MongoDB是由C語言編寫的非關系型數據庫&#xff0c;是一個基于分布式文件存儲的開源數據庫系統&#xff0c;其內容存儲形式類似JSON對象&#xff0c;它的字段值可以包含其他文檔、數組及文檔數組。在這一節中&#xff0c;我們就來回顧Python 3下MongoDB的存儲操作。 常用命令:…

第 12 屆藍橋杯 C++ 青少組中 / 高級組省賽 2021 年真題

一、選擇題 第 1 題 題目&#xff1a;下列符號中哪個在 C 中表示行注釋 ( )。 A. ! B. # C. ] D. // 正確答案&#xff1a;D 答案解析&#xff1a; 在 C 中&#xff0c;//用于單行注釋&#xff08;行注釋&#xff09;&#xff0c;從//開始到行末的內容會被編譯器忽略。選項 A…

【python】【UV】一篇文章學完新一代 Python 環境與包管理器使用指南

&#x1f40d; UV&#xff1a;新一代 Python 環境與包管理器使用指南 一、UV 是什么&#xff1f; UV 是由 Astral 團隊開發的高性能 Python 環境管理器&#xff0c;旨在統一替代 pyenv、pip、venv、pip-tools、pipenv 等工具。 1.1 UV 的主要功能 &#x1f680; 極速包安裝&…

前端性能優化2:結合HTTPS與最佳實踐,全面優化你的網站性能

點亮極速體驗&#xff1a;結合HTTPS與最佳實踐&#xff0c;為你詳解網站性能優化的道與術 在如今這個信息爆炸、用戶耐心極其有限的數字時代&#xff0c;網站的性能早已不是一個可選項&#xff0c;而是關乎生存和發展的核心競爭力。一個遲緩的網站&#xff0c;無異于在數字世界…

JavaWeb:vueaxios

一、簡介 什么是vue? 快速入門 <!-- 3.準備視圖元素 --><div id"app"><!-- 6.數據渲染 --><h1>{{ msg }}</h1></div><script type"module">// 1.引入vueimport { createApp, ref } from https://unpkg.com/vu…

Tauri聯合Vue開發中Vuex與Pinia關系及前景分析

在 TauriVue 的開發場景中&#xff0c;Vuex 和 Pinia 是兩種不同的狀態管理工具&#xff0c;它們的關系和前景可以從以下角度分析&#xff1a; 一、Vuex 與 Pinia 的關系 繼承與發展 Pinia 最初是作為 Vuex 5 的提案設計的&#xff0c;其目標是簡化 Vuex 的復雜性并更好地適配 …

Linux中的時間同步

一、時間同步服務擴展總結 1. 時間同步的重要性 多主機協作需求&#xff1a;在分布式系統、集群、微服務架構中&#xff0c;時間一致性是日志排序、事務順序、數據一致性的基礎。 安全協議依賴&#xff1a;TLS/SSL證書、Kerberos認證等依賴時間有效性&#xff0c;時間偏差可能…

【算法基礎】三指針排序算法 - JAVA

一、基礎概念 1.1 什么是三指針排序 三指針排序是一種特殊的分區排序算法&#xff0c;通過使用三個指針同時操作數組&#xff0c;將元素按照特定規則進行分類和排序。這種算法在處理包含有限種類值的數組時表現出色&#xff0c;最經典的應用是荷蘭國旗問題&#xff08;Dutch …