Linux 線程(上)

前言:大家早上中午晚上好!!今天來學習一下linux系統下所謂的線程吧!!!

一、重新理解進程,什么是進程?

1.1 圖解

其中黑色虛線部分一整塊就是進程,注意:一整塊指的是:①:從一個程序運行起來后成為進程,OS堆這個進程進行描述PCB結構、②這個PCB中的進程地址空間所映射的物理內存資源、③頁表,等等。。。總而言之:所有保證這個進程正常運行的所有資源!!

也就是說:進程是系統資源分配的基本單位,每一個進程的資源由系統分配,分配出來后的整一塊資源都屬于進程,進程相當于大家庭

1.2、Linux系統下的線程

圖解:

圖中通過第一個創建的PCB描述出來的PCB2、PCB3、PCB4、PCB5暫時可以把它理解為線程!!(注意:只是暫時把它認為是線程);

這些通過主線程描述出來的PCB2、3、4、5.....等他們共同瓜分進程地址空間,而這個地址空間就是資源的窗口(通過頁表跟物理內存建立一一映射關系),所以相當于同一進程下所有線程在同一塊資源下進行瓜分,然后這些線程就可以分別執行自己的任務!!!(所以線程相當于家庭中的每一個成員:爸爸、媽媽、爺爺、奶奶、哥哥、姐姐、等!!)

1.3線程的調度

創建并完成資源分配后,cpu就可以對一個個PCB進行調度了,讓他們分別執行代碼中的某一部分內容,各自完成自己的任務!!所以:線程是cpu調度的基本單位

二、pthread庫

2.1、pthread庫是什么先不管,先用,先寫一段代碼

寫一段代碼實現創建一個線程,并讓它執行自己的任務:

寫之前先介紹一個創建線程的接口:

首先第一個參數為:輸出型參數,需要自己定義pthread_t 類型變量,把這個變量地址傳入,創建完成后獲取到pthread_t;

第二個參數:線程屬性,不需要修改設置為nullptr即可;

第三個參數:線程的入口函數,這個線程一被調用就會從入口函數開始執行,這個函數自己定義,這個函數的返回值是void*類型,參數為void*類型;

第四個參數:給入口函數傳送的形參,如果不許要傳設置為nullptr;

創建完線程,線程跑完后要對線程進程回收;

介紹一個等待線程的接口:

第一個參數:為線程的tid,傳入哪個tid就等待哪個線程;

第二個參數為:獲取這個線程的退出信息,如果不需要設置為nullptr;

返回值int:如果等待成功返回0,如果失敗返回錯誤碼;

有了這兩個接口我們就來寫一段代碼吧:

#include <iostream>
using namespace std;
#include <pthread.h>
#include <unistd.h>
void* print(void* args)
{int cnt=10;while(cnt--){cout<<"i am a thread !!!!"<<endl;sleep(1);}return nullptr;
}
int main()
{pthread_t tid;pthread_create(&tid,nullptr,print,nullptr);//線程創建int cnt2=10;while(cnt2--){cout<<"i am a main thread!!!!"<<endl;sleep(1);}int n=pthread_join(tid,nullptr);//線程等待if(n==0)cout<<"wait success!!!"<<endl;return 0;
}

當我們編譯的時候發現報錯:

原因是因為pthread.h是動態庫,我們編譯的時候需要加上 -lpthread 才能鏈接成功:

makefile:

mythread:mythread.ccg++ -o $@ $^ -std=c++11 -lpthread   //編譯時加上-lpthread
.PHONY:clean
clean:rm -f mythread

編譯成功并生成mythread可執行程序:

運行:

我們看到主線程和分線程分別打印了10次,并成功等待了線程;

2.2 批量創建線程

上面我們創建的是一個線程,如果我們想批量創建線程呢?

那很簡單,用一個for循環創建線程同時把tid保存起來,再用一個for循環等待tid:

同時我們每個線程獲取一下pid_t(進程pid),同時每個線程獲取一下pthread_t(tid),同時用輕量級命令 :ps- aL 查看所有輕量級進程:

#include <iostream>
using namespace std;
#include <pthread.h>
#include <unistd.h>
#include <vector>
#define THREAD_NUM 5
void* print(void* args)
{cout<<"i am a thread !!!! tid is :"<<pthread_self()<<"  pid is:"<<getpid()<<endl;sleep(1);return nullptr;
}
int main()
{vector<pthread_t> tids;//批量創建線程并保存tidfor(int i=0;i<THREAD_NUM;i++){pthread_t tid;pthread_create(&tid,nullptr,print,nullptr);tids.push_back(tid);}for(int i=0;i<THREAD_NUM;i++){int n=pthread_join(tids[i],nullptr);//線程批量等待if(n==0)cout<<"wait success!!!  tid is : "<<tids[i]<<endl;}return 0;
}

編譯后運行:

同時命令行輸入ps - aL:

我們發現所有線程它的pid 都是相同的,tid是一串很長的數字且都是不相同的,用ps -aL命令查看到LWP中只一個數字15304是跟pid一樣其他都不相同!!

pthread_t tid 究竟是什么?LWP是什么?他們跟PID之間的關系是什么?還有pthread庫又是什么?

2.3重新理解線程

①:在linux系統下一般把線程稱為輕量級進程,因為在Linux內核中沒有用新的數據結構來描述線程,而是通過原有的進程PCB來描述線程的!!

②: 因此linux系統是通過LWP來對這些所謂的 “線程”?進行調度的,LWP就是:light weight process (輕量級進程的意思),所以在linux下所謂的線程:就是一個個通過(第一個創建出來的進程的PCB)描述出來的輕量級進程!

③: 那么linux系統不創建新的數據結構來描述線程,等于linux不想管理這些線程,那么誰來管理?pthread庫!!來了!pthread庫是第三方庫,當我們安裝linux操作系統的時候必須把pthread庫安裝好,這個pthread庫里有別人編寫好的一系列對線程操作的接口,且這個庫需要管理每一個用戶創建出來的線程!!!怎么管理?->先描述再組織!->pthread庫必須有一個描述線程的數據結構(TCB)->pthread庫通過對一個個TCB的管理從而管理一個個線程!!

④線程由pthread庫管理,pthread庫不屬于OS內核,所以線程不屬于OS內核!!在linux下所謂的線程是用戶級線程!!

當pthread庫調用一系列的pthread_create、pthread_join、等接口時其底層調用的其實是clone這個系統調用接口:

圖解:

所以我們可以回答上面的第一個問題了:pthread_t tid 究竟是什么:

pthread_t tid 是用戶級線程的tid!!所謂用戶級就是管理線程的pthread庫屬于用戶級!!每一個TCB有其對應的tid,用戶通過tid便可以指定對某個線程進行操作!!

然后我們還能回答第二個問題了:LWP是什么:

圖解:

我們知道每個PCB都有它的PID,linux下的“線程”是通過進程的PCB描述出來的;

當它被描述出來之后就是一個所謂的“線程”了,也就是稱為了cup調度的基本單位

而PID代表的是進程,所謂進程代表的是被分配到的整塊資源所有“線程”共享的這一個塊資源

因此所有的“線程”的PID只有一個!!這些新創建出來的“線程”所以用LWP來標志!!

如果LWP跟PID一樣說明這是第一個被創建出來的“線程”,即主線程!!!

最后cup就能通過LWP調度到每個它想調度的“線程”!!

同時我們可以回答第三個問題:pthread庫是什么:

因為linux內核不管理線程,所以linux中把線程稱之為:輕量級進程(它管理的是進程);

因為線程需要被管理,所以linux必須安裝有pthread庫;

pthread庫就是用來管理用戶創建除了的一切線程;

既然要管理所以pthread庫必須對線程進行描述,必須要有TCB結構,通過一個個TCB結構的管理實現對所有線程的管理;

因此Linux下的所謂線程其實是用戶級線程,那么我們可以這么認為:Linux下的線程=LWP(linux內核中的PCB輕量級進程標志)+pthread_t TID(用戶中的TCB線程的標志);

三、線程的控制

3.1 線程的分離

如果主線程不想等待分支線程,我們可以讓線程自己分離,分離后由OS進行;

線程分離接口:

#include<pthread.h>
int pthread_detach(pthread_t thread);
//1. 這是用來分離線程的pthread庫接口
//2. thread:你想分離的線程的tid
//3. 返回值int,如果分離成功返回0,如果分離錯誤返回錯誤碼

獲取當前線程tid接口:

#include <pthread.h>
pthread_t pthread_self(void);
//1. pthread_t 返回值如果獲取成功返回當前線程的tid

代碼:

我們分離的同時等待一下線程看看能否等待成功:

線程分離的兩種方法:線程自己分離

#include <iostream>
using namespace std;
#include <pthread.h>
#include <unistd.h>
#include <vector>
#include <cstring>
#define THREAD_NUM 5
void* print(void*args)
{pthread_detach(pthread_self());cout<<"i am a thread!!"<<endl;return nullptr;
}
int main()
{pthread_t tid;pthread_create(&tid,nullptr,print,nullptr);sleep(1);//等待1s讓線程分離完成int code=pthread_join(tid,nullptr);if(code==0){cout<<"wait success !!"<<endl;}elsecout<<"wait false!! error num is:" <<code<<"error message :"<<strerror(code)<<endl;return 0;
}

編譯運行:

線程分離成功,主線程等待失敗,返回22錯誤碼,意思是無效的參數,也就是沒有等待到對應的tid;

線程分離方法二:主線程讓他分離:

編譯運行:

結果一樣,說明成功分離分支線程;

3.2 線程的退出

線程退出的幾種方式:

①線程運行完走到return 正常退出;

②終止線程的接口:

#include<pthread.h>
void pthread_exit(void* retval);
//1. retval 可以傳入退出信息,由外部線程等待函數獲取

我們退出的同時可以傳入參數retval讓主線程獲取退出信息:

主線程等待獲取線程退出信息需要傳入一一個二級參數,因為join的第二個參數是一個輸出型參數:

void* print(void*args)
{cout<<"i am a thread!!"<<endl;pthread_exit((void*)100);return nullptr;
}
int main()
{pthread_t tid;pthread_create(&tid,nullptr,print,nullptr);sleep(1);void*retval;pthread_join(tid,&retval);(long long int)retval;printf("%d\n",retval);return 0;
}

編譯運行:

100退出碼被帶了出來;

③還有一個線程會終止的場景:當同一線程組內的任一線程出現異常,接收到終止信號,那么整個進程會崩潰,所有線程都會退出;

3.3 線程的取消

主線程可以取消一個線程,前提是這個線程已經被啟動:

取消線程的接口:

#include<pthread.h>
int pthread_cancel( pthread_t thread);
//1. 取消一個線程,前提這個線程被啟動
//2. thread : 需要取消的線程的tid
//3. 如果成功返回0,失敗返回一個錯誤碼
void* print(void*args)
{while(true){cout<<"i am a thread!!"<<endl;sleep(1);}return nullptr;
}
int main()
{pthread_t tid;pthread_create(&tid,nullptr,print,nullptr);int code= pthread_cancel(tid);if(code==0){cout<<"取消成功"<<endl;}elsecout<<"取消失敗"<<endl;//sleep(1);//線程已被啟動pthread_join(tid,nullptr);return 0;
}

運行:

四、總結線程的特點

4.1 、線程是系統調度的基本單位,進程是資源分配的基本單位;
4.2、線程的粒度小于進程,占用資源更少,因此通常多線程比多進程更高效;
4.3、線程沒有獨立的地址空間,線程是劃分進程的地址空間,本質上使用的是同一地址空間!線程不擁有系統資源,線程共享進程的資源!
4.4、大量的計算使用多線程和多進程都可以實現并行/并發處理,區別在于線程的資源消耗小于進程,線程的穩定性不如進程,因此需要具體更細致的需求場景;
4.5、線程除了獨立的棧空間和一組寄存器(保存線程的上下文)其他線程看不到之外其他所有資源都是所有線程共享的;
4.6、線程的通信速度更快,切換更快,因為他們在同地址空間內,且還共享了很多其他進程資源,比如頁表指針,這些是不需要切換的!

今天的分享就到這里!如果對你有所幫助記得點贊收藏+關注哦!!謝謝!!!

咱下期見!!!

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

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

相關文章

Java API學習筆記

一.類 1. String 類 不可變性&#xff1a;String對象創建后不可修改&#xff0c;每次操作返回新對象 String str "Hello"; str.length(); str.charAt(0); str.substring(1, 4); str.indexOf("l"); str.equals("hel…

醫療信息系統安全防護體系的深度構建與理論實踐融合

一、醫療數據訪問系統的安全挑戰與理論基礎 1.1 系統架構安全需求分析 在醫療信息系統中&#xff0c;基于身份標識的信息查詢功能通常采用分層架構設計&#xff0c;包括表現層、應用層和數據層。根據ISO/IEC 27001信息安全管理體系要求&#xff0c;此類系統需滿足數據保密性…

5.18本日總結

一、英語 復習list3list28 二、數學 學習14講部分內容&#xff0c;1000題13講部分 三、408 學習計網5.3剩余內容 四、總結 計網TCP內容比較重要&#xff0c;連接過程等要時常復習&#xff1b;高數學到二重積分對定積分的計算相關方法有所遺忘&#xff0c;需要加強鞏固。…

MATLAB2025新功能

截至2023年9月&#xff0c;MATLAB官方尚未公布2025版本的具體更新內容。根據歷史更新規律和技術發展趨勢&#xff0c;未來版本可能會在以下方面進行優化&#xff1a; AI與深度學習增強 可能新增自動化模型壓縮工具強化生成式AI模型支持改進的ONNX格式轉換接口 性能提升 矩陣運…

算法題(149):矩陣消除游戲

審題&#xff1a; 本題需要我們找到消除矩陣行與列后可以獲得的最大權值 思路&#xff1a; 方法一&#xff1a;貪心二進制枚舉 這里的矩陣消除時&#xff0c;行與列的消除會互相影響&#xff0c;所以如果我們先統計所有行和列的總和&#xff0c;然后選擇消除最大的那一行/列&am…

Uniapp、Flutter 和 React Native 全面對比

文章目錄 前言Uni-app、Flutter 和 React Native 跨平臺框架對比報告1. 性能對比2. 跨平臺能力3. 學習曲線4. 社區生態與第三方庫5. 原生能力擴展6. UI 渲染能力7. 企業支持與典型使用場景8. 開發效率與工具鏈 前言 將對 Uniapp、Flutter 和 React Native 進行全面對比&#x…

JAVA SE 多線程(上)

文章目錄 &#x1f4d5;1. Thread類及常見方法??1.1 創建線程??1.2 Thread 的常見構造方法??1.3 Thread 的幾個常見屬性??1.4 啟動一個線程---start()??1.5 中斷一個線程---interrupt()??1.6 等待一個線程---join()??1.7 獲取當前線程引用??1.8 休眠當前線程 &…

Linux云計算訓練營筆記day10(MySQL數據庫)

Linux云計算訓練營筆記day10&#xff08;MySQL數據庫&#xff09; 目錄 Linux云計算訓練營筆記day10&#xff08;MySQL數據庫&#xff09;ifnull別名聚合函數group byHAVING 子查詢關聯查詢 ifnull 在DQL語句中可以使用函數或表達式 函數 IFNULL(arg1,arg2) 如果arg1為NULL,函…

上位機知識篇---流式Web服務器模式的實現

文章目錄 前言 前言 本文簡單介紹了流式Web服務器模式的實現。

Dify與n8n全面對比指南:AI應用開發與工作流自動化平臺選擇【2025最新】

Dify與n8n全面對比指南&#xff1a;AI應用開發與工作流自動化平臺選擇【2025最新】 隨著AI技術與自動化工具的迅速發展&#xff0c;開發者和企業面臨著多種平臺選擇。Dify和n8n作為兩個備受關注的自動化平臺&#xff0c;分別專注于不同領域&#xff1a;Dify主要面向AI應用開發&…

day19-線性表(順序表)(鏈表I)

一、補充 安裝軟件命令&#xff1a; sudo apt-get install (軟件名) 安裝格式化對齊&#xff1a;sudo apt-get install clang-format內存泄漏檢測工具&#xff1a; sudo apt-get install valgrind 編譯后&#xff0c;使用命令 valgrind ./a.out 即可看內存是…

AI:人形機器人一定是人的形狀嗎?

本文將從技術角度分析人形機器人是否必須是人的形狀&#xff0c;以及人形與非人形機器人在適用場合、優缺點上的差異。以下是詳細解答&#xff1a; 人形機器人一定是人的形狀嗎&#xff1f; 不&#xff0c;人形機器人&#xff08;Humanoid Robot&#xff09;在技術上通常指外…

布隆過濾器和布谷鳥過濾器

原文鏈接&#xff1a;布隆過濾器和布谷鳥過濾器 布隆過濾器 介紹 布隆過濾器&#xff08;Bloom Filter&#xff09;是 1970 年由布隆提出的。它實際上是一個很長的二進制向量和一系列隨機映射函數&#xff0c;檢查值是“可能在集合中”還是“絕對不在集合中” 空間效率高&a…

無需配置光貓,使用網管交換機配合路由器的IPTV功能實現單線復用

一、背景 弱電箱和電視柜只預留了一根網線&#xff0c;路由器放在電視柜&#xff0c;想實現既可以上網又可以正常觀看iptv&#xff0c;本文提供了一種方法。 二、準備工作 1、帶iptv功能的路由器&#xff1b;2、水星sg105pro網管交換機&#xff1b;3、網線若干&#xff1b; …

深入理解SpringBoot中的SpringCache緩存技術

深入理解SpringBoot中的SpringCache緩存技術 引言 在現代應用開發中&#xff0c;緩存技術是提升系統性能的重要手段之一。SpringBoot提供了SpringCache作為緩存抽象層&#xff0c;簡化了緩存的使用和管理。本文將深入探討SpringCache的核心技術點及其在實際業務中的應用場景。…

2025認證杯數學建模A題思路+代碼+模型:小行星軌跡預測

2025認證杯數學建模A題思路代碼模型&#xff0c;詳細內容見文末名片 近地小行星&#xff08; Near Earth Asteroids, NEAs &#xff09;是軌道相對接近地球的小行 星&#xff0c;它的正式定義為橢圓軌道的近日距不大于 1.3 天文單位&#xff08; AU &#xff09;的小行星。 …

LeetCode Hot100刷題——輪轉數組

56. 輪轉數組 給定一個整數數組 nums&#xff0c;將數組中的元素向右輪轉 k 個位置&#xff0c;其中 k 是非負數。 示例 1: 輸入: nums [1,2,3,4,5,6,7], k 3 輸出: [5,6,7,1,2,3,4] 解釋: 向右輪轉 1 步: [7,1,2,3,4,5,6] 向右輪轉 2 步: [6,7,1,2,3,4,5] 向右輪轉 3 步: …

「Mac暢玩AIGC與多模態41」開發篇36 - 用 ArkTS 構建聚合搜索前端頁面

一、概述 本篇基于上一節 Python 實現的雙通道搜索服務&#xff08;聚合 SearxNG 本地知識庫&#xff09;&#xff0c;構建一個完整的 HarmonyOS ArkTS 前端頁面。用戶可在輸入框中輸入關鍵詞&#xff0c;實時查詢本地服務 http://localhost:5001/search?q...&#xff0c;返…

開源鴻蒙北向源碼開發: 5.0kit化相關sdk編譯

5.0kit化可以在編譯系統sdk時添加,將你的kit文件加入編譯使得最終生成的sdk包含kits文件 修改編譯腳本 修改build倉里面的構建腳本文件,添加kits目錄腳本命令 社區的build腳本已經有kits編譯功能了,只需要把你的kits目錄新增的kit拷貝到社區倉interface倉了,和社區的都一起編…

題單:漢諾塔問題

題目描述 如下圖所示&#xff0c;設有 nn 個大小不等的中空圓盤&#xff0c;按照從小到大的順序疊套在立柱 A 上&#xff0c;另有兩根立柱 B 和 C 。 現在要求把全部圓盤從 A 柱&#xff08;稱為源柱&#xff09;移到 C 柱&#xff08;稱為目標柱&#xff09;&#xff0c;移動…