【Linux】:線程(二)互斥

互斥與同步

  • 一.線程的局部存儲
  • 二.線程的分離
  • 三.互斥
    • 1.一些概念
    • 2.上鎖
    • 3.鎖的原理
    • 4.死鎖

一.線程的局部存儲

例子

在這里插入圖片描述

在這里插入圖片描述

可以看到全局變量是所有線程共享的,如果我們想要每個線程都單獨訪問g_val怎么辦呢?其實我們可以在它前面加上__thread修飾。

在這里插入圖片描述

在這里插入圖片描述

這就相當于把g_val從全局變量去儲存到了局部儲存里。每個線程可以單獨訪問自己的g_val。(注意__thread只能定義內置類型)

在這里插入圖片描述

二.線程的分離

默認情況下,新創建的線程是joinable的,線程退出后,需要對其進行pthread_join操作,否則無法釋放資源,從而造成系統泄漏。

如果不關心線程的返回值,join是一種負擔(因為它會阻塞我們的主線程),這個時候,我們可以告訴系統,當線程退出時,自動釋放線程資源。

在這里插入圖片描述

在這里插入圖片描述

這個函數在主函數和當前線程里都可以使用。

三.互斥

1.一些概念

臨界資源:多線程執行流共享的資源就叫做臨界資源 臨界區:每個線程內部,訪問臨界資源的代碼,就叫做臨界區。
互斥:任何時刻,互斥保證有且只有一個執行流進入臨界區,訪問臨界資源,通常對臨界資源起保護作用。
原子性(后面討論如何實現):不會被任何調度機制打斷的操作,該操作只有兩態,要么完成,要么未完成。

大部分情況,線程使用的數據都是局部變量,變量的地址空間在線程棧空間內,這種情況,變量歸屬單個線程,其他線程無法獲得這種變量。

但有時候,很多變量都需要在線程間共享,這樣的變量稱為共享變量,可以通過數據的共享,完成線程之間的交互。

多個線程并發的操作共享變量,會帶來一些問題。

例如,一個搶票系統

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>int ticket=100;
void *route(void *arg)
{char *id = (char*)arg;while(1) {if (ticket>0) {usleep(1000);printf("%s sells ticket:%d\n", id, ticket);ticket--;} else {break;}}
} 
int main()
{pthread_t t1, t2, t3, t4;pthread_create(&t1, NULL, route, (void*)"thread 1");pthread_create(&t2, NULL, route, (void*)"thread 2");pthread_create(&t3, NULL, route, (void*)"thread 3");pthread_create(&t4, NULL, route, (void*)"thread 4");pthread_join(t1, NULL);pthread_join(t2, NULL);pthread_join(t3, NULL);pthread_join(t4, NULL);return 0;
}

在這里插入圖片描述

可以看到已經搶到了負數,很明顯是不符合實際的。這是因為在執行打印ticket操作時,操作系統需要從CPU里讀取ticket數據,而當一個線程已經打印了ticket=0后,再執行了減減操作,ticket變為了-1,將ticket的值再CPU里更新;這時切換到了另一個線程,而該線程又恰好正要執行打印ticket操作,那么它從CPU里讀取了數據,打印出來就為了負數。

要解決以上問題,需要做到三點:

1.代碼必須要有互斥行為:當代碼進入臨界區執行時,不允許其他線程進入該臨界區。
2.如果多個線程同時要求執行臨界區的代碼,并且臨界區沒有線程在執行,那么只能允許一個線程進入該臨界區。
3.如果線程不在臨界區中執行,那么該線程不能阻止其他線程進入臨界區。

要做到這三點,本質上就是需要一把鎖。Linux上提供的這把鎖叫互斥量。

在這里插入圖片描述

2.上鎖

創建鎖

在這里插入圖片描述

調用函數時可能會出現以下情況:

1.互斥量處于未鎖狀態,該函數會將互斥量鎖定,同時返回成功
2.發起函數調用時,其他線程已經鎖定互斥量,或者存在其他線程同時申請互斥量,但沒有競爭到互斥量,那么pthread_ lock調用會陷入阻塞(執行流被掛起),等待互斥量解鎖。

加鎖

在這里插入圖片描述

在這里插入圖片描述

修改代碼

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sched.h>pthread_mutex_t mutex=PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP;//初始化
int ticket=100;
void *route(void *arg)
{char *id =(char*)arg;while(1) {pthread_mutex_lock(&mutex);//上鎖if(ticket>0) {usleep(1000);printf("%s sells ticket:%d\n", id, ticket);ticket--;pthread_mutex_unlock(&mutex);//解鎖} else {pthread_mutex_unlock(&mutex);//解鎖break;}}
} 
int main()
{pthread_t t1, t2, t3, t4;pthread_create(&t1, NULL, route, (void*)"thread 1");pthread_create(&t2, NULL, route, (void*)"thread 2");pthread_create(&t3, NULL, route, (void*)"thread 3");pthread_create(&t4, NULL, route, (void*)"thread 4");pthread_join(t1, NULL);pthread_join(t2, NULL);pthread_join(t3, NULL);pthread_join(t4, NULL);pthread_mutex_destroy(&mutex);//銷毀return 0;
}

在這里插入圖片描述

我們發現票數問題得到了解決,但是票全被一個線程搶走了,這是怎么回事呢?其實是由于不同線程對于鎖的競爭能力是不同的,這里當線程2釋放鎖后,馬上又去申請了鎖,導致鎖一直被線程2拿著,出現了線程饑餓問題。我們可以在外面加上sleep函數,讓每個線程釋放鎖后休息一段時間,避免鎖一直在某一個線程上。

在這里插入圖片描述

在這里插入圖片描述

3.鎖的原理

經過上面的例子,大家已經意識到單純的 i++ 或者 ++i 都不是原子的,有可能會有數據一致性問題。

為了實現互斥鎖操作,大多數體系結構都提供了swap或exchange指令,該指令的作用是把寄存器和內存單元的數據相交換,由于只有一條指令,保證了原子性,即使是多處理器平臺,訪問內存的 總線周期也有先后,一個處理器上的交換指令執行時另一個處理器的交換指令只能等待總線周期。 現在我們把lock和unlock的偽代碼改一下。

在這里插入圖片描述

movb語句是把al寄存器置零。

xchgb語句就是把al寄存器里的數據交換與內存里的mutex(1)變量進行一次交換(此時mutex就變為了0)。注意mutex是所有線程共享,也就是說其實1只有一份,當第一個進程將mutex里的1交換走后,后面的線程就無法拿到1,也就是上鎖了。

4.死鎖

死鎖是指在一組進程中的各個進程均占有不會釋放的資源,但因互相申請被其他進程所站用不會釋放的資源而處于的一種永久等待狀態。

死鎖的必要條件

互斥條件:一個資源每次只能被一個執行流使用。
請求與保持條件:一個執行流因請求資源而阻塞時,對已獲得的資源保持不放。
不剝奪條件:一個執行流已獲得的資源,在末使用完之前,不能強行剝奪。
循環等待條件:若干執行流之間形成一種頭尾相接的循環等待資源的關系。

避免死鎖

破壞死鎖的四個必要條件。
加鎖順序一致。
避免鎖未釋放的場景。
資源一次性分配。

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

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

相關文章

網絡入門---網絡編程初步認識和實踐(使用udp協議)

目錄標題 前言準備工作udpserver.hpp成員變量構造函數初始化函數(socket,bind)start函數(recvfrom) udpServer.ccudpClient.hpp構造函數初始化函數run函數(sendto) udpClient.cc測試 前言 在上一篇文章中我們初步的認識了端口號的作用&#xff0c;ip地址和MAC地址在網絡通信時…

Python實現GUI圖片瀏覽程序

Python實現GUI圖片瀏覽程序 下面程序需要pillow庫。pillow是 Python 的第三方圖像處理庫&#xff0c;需要安裝才能實用。pillow是PIL&#xff08; Python Imaging Library&#xff09;基礎上發展起來的&#xff0c;需要注意的是pillow庫安裝用pip install pillow&#xff0c;導…

【改進YOLOv8】融合可擴張殘差(DWR)注意力模塊的小麥病害檢測系統

1.研究背景與意義 項目參考AAAI Association for the Advancement of Artificial Intelligence 研究背景與意義&#xff1a; 隨著計算機視覺技術的快速發展&#xff0c;深度學習在圖像識別和目標檢測領域取得了巨大的突破。其中&#xff0c;YOLO&#xff08;You Only Look O…

基于SSM框架的網上商城系統的設計與實現

完整下載&#xff1a;基于SSM框架的網上商城系統的設計與實現 基于SSM框架的網上商城系統的設計與實現 "Design and Implementation of an Online Shopping Mall System Based on SSM Framework" 目錄 目錄 2 摘要 3 關鍵詞 3 第一章 系統設計 4 1.1 系統需求分析 4 …

TruLens RAG Triad 學習

TruLens RAG Triad 學習 0. 背景1. RAG 三元組2. TruLens 快速入門2-1. 安裝依賴2-2. 初始化 OpenAI 認證信息2-3. 獲取數據2-4. 創建向量存儲2-5. 從頭構建自定義 RAG2-6. 設置反饋函數2-7. 構建應用程序2-8. 運行應用程序0. 背景 近年來,RAG 架構已成為為大型語言模型 (LLM…

Nexus搭建npm私庫(角色管理、上傳腳本)

安裝Nexus 官網下載 https://www.sonatype.com/products/sonatype-nexus-oss-download 進入官網下載&#xff0c;最新下載方式需要輸入個人信息才能下載了 選擇對應的系統進行下載 Windows 推薦也下載 UNIX 版本&#xff08;Windows 版本配置比較難改&#xff09; 如果沒有下…

chmod 在Linux原生應用開發過程中的簡單應用

chmod命令實質上是用來修改文件或目錄的訪問權限的命令。它通過修改文件或目錄的訪問控制列表&#xff08;Access Control List&#xff0c;ACL&#xff09;來實現權限的更改。 在Linux系統中&#xff0c;每個文件或目錄都有一個表示其權限的數字值&#xff0c;即用三個八進制…

Qt 中的窗口類

目錄 QWidget 設置父對象 窗口位置 測試代碼 窗口尺寸 窗口標題和圖標 信號 槽函數 QDialog 常用API QDialog的子類 QMessageBox QFileDialog QFontDialog QFontDialog類的靜態API QColorDialog 顏色類 QColor 靜態API函數 測試代碼 QInputDialog 靜態函數…

谷歌AI新篇章:Gemini引領股價飆升,挑戰OpenAI霸主地位

導語&#xff1a; “ 在人工智能領域的一場激烈角逐中&#xff0c;谷歌母公司Alphabet以其全新AI大模型Gemini&#xff0c;于周四收盤時引爆股市&#xff0c;股價激增5.3%&#xff0c;市值一日增長超800億美元。這一躍升不僅展示了谷歌在技術創新上的決心&#xff0c;也是對微軟…

GPT4停止訂閱付費了怎么辦? 怎么升級ChatGPT plus?提供解決方案

11月中旬日OpenAI 暫時關閉所有的升級入口之后&#xff0c;很多小伙伴就真的在排隊等待哦。其實有方法可以繞開排隊&#xff0c;直接付費訂閱升級GPT的。趕緊用起來立馬“插隊”成功&#xff01;親測~~~ 一、登錄ChatGPT賬號 1、沒有賬號可以直接注冊一個&#xff0c;流程超級…

前端使用視頻作為背景圖的方法

實現思路 通過 video source 引入視頻&#xff0c;并對視頻播放屬性進行設置&#xff0c;再通過 css 使視頻覆蓋背景即可。 代碼 <!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>有開發問題可聯系作者</title>…

學習git后,真正在項目中如何使用?

文章目錄 前言下載和安裝Git克隆遠程倉庫PyCharm鏈接本地Git創建分支修改項目工程并提交到本地倉庫推送到遠程倉庫小結 前言 網上學習git的教程&#xff0c;甚至還有很多可視化很好的git教程&#xff0c;入門git也不是什么難事。但我發現&#xff0c;當我真的要從網上克隆一個…

ubuntu18.04安裝pcl1.11.1

一、安裝pcl1.11.1依賴 sudo apt-get update sudo apt-get install git build-essential linux-libc-dev sudo apt-get install cmake cmake-gui sudo apt-get install libusb-1.0-0-dev libusb-dev libudev-dev sudo apt-get install mpi-default-dev openmpi-bin openmpi-…

SQL事務的開啟,提交和回滾

在處理數據庫數據的時候會出現一種情況就是我們刪除兩個關聯的表其中一個表的信息&#xff0c;另一個表也需要改動&#xff0c;但是我們SQL語句在同時更改兩個表的同時&#xff0c;難免會出現一個表修改成功&#xff0c;另一個出現錯誤&#xff0c;這時候表與表之間就會出現矛盾…

webrtc網之sip轉webrtc

OpenSIP是一個開源的SIP&#xff08;Session Initiation Protocol&#xff09;服務器&#xff0c;它提供了一個可擴展的基礎架構&#xff0c;用于建立、終止和管理VoIP&#xff08;Voice over IP&#xff09;通信會話。SIP是一種通信協議&#xff0c;用于建立、修改和終止多媒體…

geolife 筆記:將所有軌跡放入一個DataFrame

單條軌跡的處理&#xff1a;geolife筆記&#xff1a;整理處理單條軌跡-CSDN博客 1 加載數據 import pandas as pd import numpy as np import datetime as dt import osdata_dir Geolife Trajectories 1.3/Data/ 1.1 列出所有文件夾 dirlist os.listdir(data_dir) dirlist…

Esxi登錄超時:“由于不活動超時,您已被注銷“,修改UserVars.HostClientSessionTimeout為0永不超時

Esxi登錄超時:“由于不活動超時&#xff0c;您已被注銷”,修改UserVars.HostClientSessionTimeout為0永不超時 UserVars.HostClientSessionTimeout0永不超時 Esxi網頁登錄后,一段時間不操作就會被注銷 提示: 由于不活動超時&#xff0c;您已被注銷 主機→管理→系統→高級設…

【linux】查看CPU和內存信息

之前咱們一起學習了查看內存的和CPU的命令。 ?mpstat &#xff1a; 【linux】 mpstat 使用 uptime&#xff1a;【Linux】 uptime命令使用 CPU的使用率&#xff1a;【linux】查看CPU的使用率 nmon &#xff1a;【linux】nmon 工具使用 htop &#xff1a;【linux】htop 命令…

文件格式對齊、自定義快捷鍵、idea

文件格式對齊 Shift Alt F 自動格式化代碼的快捷鍵&#xff08;如何配置自動格式化&#xff09; 日常編碼必備idea快捷鍵 [VS Code] 入門-自定鍵盤快捷鍵 文件格式對齊 文件格式對齊通常是通過編輯器或IDE提供的快捷鍵或命令完成的。以下是一些常見編輯器和IDE中進行文件…

四、C#筆記

/// <summary> /// 第七章&#xff1a;創建并管理類和對象 /// </summary> namespace Chapter7 { class Program { public static int Num 0;//7.6.1創建共享字段 public const double PI 3.1415926;//7.6.2使用const關鍵字創建靜態字段…