21.Linux 線程庫的使用與封裝

在linux內核中并沒有線程的概念,只有輕量級進程LWP的概念,linux下的線程都是是由LWP進行模擬實現的。因此linux操作系統中不會提供線程的相關接口,只會提供輕量級線程的接口(如vfork,clone等)。但是在我們的學習過程中實際上學到的都是線程的概念,故而linux的設計者在用戶和操作系統之間將輕量級進程的的系統調用進行了封裝成庫并提供給了用戶,這樣的線程我們稱之為用戶級線程庫

接下來我們介紹一下linux系統中線程庫的使用與封裝。

一、線程庫的使用

1.1 POSIX線程庫

在linux中,與線程有關的函數構成了一個完整的系列,絕大多數函數的名字都是以pthread_ 打頭的。要想使用這些函數庫,要通過引入頭文件 <pthread.h>,而在鏈接這些線程函數庫時要使用編譯器命令的-lpthread選項。

1.2 線程庫的接口

1.2.1 線程創建

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void* (*start_routine)(void*), void *arg);
//功能:創建一個新的線程
//參數://thread:輸出型參數,用以返回線程ID//attr:設置線程的屬性,attr為NULL表示使?默認屬性//start_routine:是個函數地址,即線程啟動后要執?的函數,它的返回值也可以是任意類型(整型,字符型,對象)//arg:傳給線程啟動函數的參數,可以是任意類型(整型,字符型,對象……)
//返回值:成功返回0,失敗返回錯誤碼

示例:

#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;void* run(void* arg)
{while(true){cout<<"new thread :"<<(char* )arg<<endl;sleep(1);}
}int main()
{pthread_t thread1;pthread_create(&thread1,nullptr,run,(void*)"new thread1");pthread_t thread2;pthread_create(&thread2,nullptr,run,(void*)"new thread2");while(true){cout<<"main thread"<<endl;sleep(1);}return 0;
}

注意:
? 新線程和主線程的運行順序是不確定的。
? 多線程的調度時間是基于對進程的時間片進行瓜分。
? 多個線程向同一個文件(這里是顯示器)進行寫入時,在不加保護的情況下是會發生重入的,也就會產生數據不一致問題。
? 線程是共享進程地址空間的。
? 線程出現異常會導致當前進程的其他線程全部崩潰。

1.2.2 線程終止

和進程一樣,線程創建之后也是要被等待和回收的!

如果需要只終止某個線程而不終止整個進程的話,有三種方法:
? 線程執行函數return。這種方法對主線程不適用,main函數return相當于調用exit。
? 線程可以調用pthread_exit終止自己。

void pthread_exit(void *value_ptr);
//功能:線程終?
//參數://value_ptr:線程退出時的返回值。

需要注意pthread_exit或者return返回的指針所指向的內存單元必須是全局的或者是用malloc分配的,不能在線程函數的棧上分配。因為當其它線程得到這個返回指針時線程函數已經退出了。

? 一個線程可以調用pthread_cancel終止同一進程中的另一個線程。

int pthread_cancel(pthread_t thread);
//功能:取消一個執?中的線程
//參數://thread:要取消的線程ID
//返回值:成功返回0,失敗返回錯誤碼

1.2.3 線程等待

為什么需要線程等待?
因為已經退出的線程其空間沒有被釋放,仍然在進程的地址空間內。而創建新的線程不會復用剛才退出線程的地址空間。

int pthread_join(pthread_t thread, void **value_ptr);
//功能:等待線程結束
//參數://thread:線程ID//value_ptr:用以保存線程的返回值的地址
//返回值:成功返回0;失敗返回錯誤碼

調用該函數的線程將掛起等待,直到id為thread的線程終止。

thread線程以不同的方法終止,通過pthread_join得到的終止狀態是不同的:
? 如果thread線程通過return返回,那么value_ptr所指向的單元里存放的是thread線程函數的返回值。
? 如果thread線程被別的線程調用pthread_cancel異常終掉,value_ptr所指向的單元里存放的是常數PTHREAD_CANCELED((void*)-1)。
? 如果thread線程是自己調用pthread_exit終止的,value_ptr所指向的單元存放的是傳給pthread_exit的參數。
? 如果對thread線程的終止狀態不感興趣,則可以傳NULL給value_ptr參數。

在這里插入圖片描述
示例:

#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;void* run1(void* arg)
{int count=5;while(count--){cout<<"new thread :"<<(char* )arg<<endl;sleep(1);}pthread_exit((void*)100);cout<<"退出成功!"<<endl;}void* run2(void* arg)
{while(true){cout<<"new thread :"<<(char* )arg<<endl;sleep(1);}
}
int main()
{//創建線程1,線程2pthread_t thread1;pthread_create(&thread1,nullptr,run1,(void*)"new thread1");pthread_t thread2;pthread_create(&thread2,nullptr,run2,(void*)"new thread2");sleep(3);//取消線程2pthread_cancel(thread2);cout<<"取消成功!"<<endl;//線程等待void *ret1,*ret2;pthread_join(thread1,&ret1);pthread_join(thread2,&ret2);cout<<"等待成功!"<<endl;cout<<"ret1 :"<<(long long)ret1<<";ret2 :"<<(long long)ret2<<endl;return 0;
}
caryon@VM-24-10-ubuntu:~/linux/thread$ ./thread 
new thread :new thread1
new thread :new thread2
new thread :new thread1
new thread :new thread2
new thread :new thread1
new thread :new thread2
取消成功!
new thread :new thread1
new thread :new thread1
等待成功!
ret1 :100;ret2 :-1

在多執行流的情況下,主執行流往往是最后退出的!

1.2.4 線程分離

默認情況下,新創建的線程是joinable的,線程退出后需要對其進型pthread_join操作,否則無法釋放資源從而造成系統泄漏。如果不關心線程的返回值的話join就是一種負擔。這個時候我們可以告訴系統,當線程退出時,自動釋放線程資源。

int pthread_detach(pthread_t thread);
//功能:進行線程分離
//參數://thread:被分離的線程ID
//返回值:成功返回0;失敗返回錯誤碼

可以是線程組內其他線程對目標線程進型分離,也可以是線程自己分離。joinable和分離是沖突的,一個線程不能既是joinable又是分離的。

#include <iostream>
#include <unistd.h>
#include <pthread.h>
using namespace std;void *thread_run(void *arg)
{printf("%s\n", (char *)arg);return NULL;
}
int main(void)
{//創建線程pthread_t tid;pthread_create(&tid, NULL, thread_run, (void*)"thread1 run...") ;//分離線程pthread_detach(tid);int ret = 0;sleep(1); if (pthread_join(tid, NULL) == 0){printf("pthread wait success\n");ret = 0;}else{printf("pthread wait failed\n");ret = 1;}return ret;
}
caryon@VM-24-10-ubuntu:~/linux/thread$ ./thread 
thread1 run...
pthread wait failed

二、線程id與進程空間布局

在線程創建、終止、等待時我們都有注意到pthread_t thread(線程id)這個參數的存在,這個id表示的是什么意思呢?

#include<iostream>
#include<pthread.h>
using namespace std;void* run(void* args)
{while(true);return nullptr;    
}
int main()
{pthread_t tid;pthread_create(&tid,nullptr,run,(void*)"newthread");printf("0x%lx\n",tid);return 0;
}
caryon@VM-24-10-ubuntu:~/linux/thread$ ./thread 
0x7fae58e006c0

我們可以看到這個id的值是一個地址,那這個地址是什么地址呀?

通過ldd查看我們所形成的可執行程序是依賴pthread庫的,這個庫在程序運行時會加載到內存中,進而映射到進程地址空間里被所有的線程所共享。我們又知道linux系統中只有輕量級進程,線程是使用輕量級進程模擬實現的。但是用戶想要查看當前線程的屬性怎么辦呢?
linux內核中是存在這些屬性的,但是Linux中不存在線程呀!這需要內核層與用戶層進行解耦啊!故而線程的相關屬性也是由pthread庫進行維護的,用戶可以通過pthread庫來查看線程的屬性。

所以這個地址就是該線程的屬性所對應的進程地址空間中的地址!

我們可以使用下面這個函數獲取當前線程的id:

pthread_t pthread_self(void);

在這里插入圖片描述
上圖就是進程地址空間中的tcb了。
? struct pthread
這個結構里面一定封裝了LWP。
? 線程棧
進程地址空間中的棧是主線程的棧,新線程的棧是通過動態申請創建的。
? 線程局部存儲
理論上來說全部變量是所有線程所共享的,即他們所訪問的全局變量的地址是相同的!但是在聲明全局變量是加上__thread進行修飾(修飾的只能是內置類型)就表示給每個線程都來一份,即線程的局部性存儲。

#include<iostream>
#include<pthread.h>
using namespace std;__thread int count=100;void* run(void* args)
{printf("new thread &count : %p\n",&count);return nullptr;    
}int main()
{pthread_t tid;pthread_create(&tid,nullptr,run,(void*)"newthread");pthread_join(tid,nullptr);printf("main thread &count : %p\n",&count);return 0;
}
caryon@VM-24-10-ubuntu:~/linux/thread$ ./thread 
new thread &count : 0x71e23b2006bc
main thread &count : 0x71e23ba8e4fc

三、線程的封裝

#pragma once#include <iostream>
#include <string>
#include <pthread.h>
#include <functional>
#include <sys/types.h>
#include <unistd.h>using func_t = std::function<void()>;
static int number = 1;
enum class TSTATUS
{NEW,RUNNING,STOP
};class Thread
{
private:// 成員方法!static void *Routine(void *args){Thread *t = static_cast<Thread *>(args);t->_status = TSTATUS::RUNNING;t->_func();return nullptr;}void EnableDetach() { _joinable = false; }bool IsJoinable() { return _joinable; }
public:Thread(func_t func) : _func(func), _status(TSTATUS::NEW), _joinable(true){_name = "Thread-" + std::to_string(number++);_pid = getpid();}bool Start(){if (_status != TSTATUS::RUNNING){int n = ::pthread_create(&_tid, nullptr, Routine, this); if (n != 0)return false;return true;}return false;}bool Stop(){if (_status == TSTATUS::RUNNING){int n = ::pthread_cancel(_tid);if (n != 0)return false;_status = TSTATUS::STOP;return true;}return false;}bool Join(){if (_joinable){int n = ::pthread_join(_tid, nullptr);if (n != 0)return false;_status = TSTATUS::STOP;return true;}return false;}void Detach(){EnableDetach();pthread_detach(_tid);}~Thread(){}
private:std::string _name;pthread_t _tid;pid_t _pid;bool _joinable; // 是否是分離的,默認不是func_t _func;TSTATUS _status;
};

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

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

相關文章

Aliyun CTF 2025 web 復現

文章目錄 ezoj打卡OKoffens1veFakejump server ezoj 進來一看是算法題&#xff0c;先做了試試看,gpt寫了一個高效代碼通過了 通過后沒看見啥&#xff0c;根據頁面底部提示去/source看到源代碼&#xff0c;沒啥思路&#xff0c;直接看wp吧&#xff0c;跟算法題沒啥關系,關鍵是去…

《鴻蒙系統下AI模型訓練加速:時間成本的深度剖析與優化策略》

在當今數字化浪潮中&#xff0c;鴻蒙系統憑借其獨特的分布式架構與強大的生態潛力&#xff0c;為人工智能的發展注入了新的活力。隨著AI應用在鴻蒙系統上的日益普及&#xff0c;如何有效降低模型訓練的時間成本&#xff0c;成為了開發者與研究者們亟待攻克的關鍵課題。這不僅關…

Git使用(一)--如何在 Windows 上安裝 Git:詳細步驟指南

如果你想在 Windows 機器上安裝 Git&#xff0c;可以按照以下詳細指南進行操作。 第一步&#xff1a;下載 Git 可通過官網下載 適用于 Windows 的 Git 最新版本。 如果下載速度較慢&#xff0c;可以通過下面提供的百度網盤 鏈接下載安裝包&#xff0c; https://git-scm.com/d…

基于Prometheus+Grafana的Deepseek性能監控實戰

文章目錄 1. 為什么需要專門的大模型監控?2. 技術棧組成2.1 vLLM(推理引擎層)2.2 Prometheus(監控采集層)2.3 Grafana(數據可視化平臺)3. 監控系統架構4. 實施步驟4.1 啟動DeepSeek-R1模型4.2 部署 Prometheus4.2.1 拉取鏡像4.2.2 編寫配置文件4.2.3 啟動容器4.3 部署 G…

本地Git倉庫搭建(DevStar)與Git基本命令

本地Git倉庫搭建&#xff08;DevStar&#xff09;與Git基本命令 實驗環境搭建平臺Git基本命令的使用本地倉庫的創建代碼提交代碼合并版本發布 總結 實驗環境 搭建平臺 按照DevStar的Github倉庫要求&#xff0c;在終端中執行下列命令&#xff0c;即可成功安裝DevStar到本地部署…

stm32 藍橋杯 物聯網 獨立鍵盤的使用

在藍橋杯物聯網平臺里面&#xff0c;有5個外接設備&#xff0c;其中有一個就是6個獨立按鍵。首先&#xff0c;我們先看一下按鍵有關的電路圖。 電路圖與cubemx設定 由圖可見&#xff0c;獨立鍵盤組由兩行三列構成&#xff0c;我們通過行列來鎖定要訪問的獨立按鍵在哪。ROW1掛…

set_clock_groups

一、命令參數與工具處理邏輯 核心參數定義 參數定義工具行為工具兼容性-asynchronous完全異步時鐘組&#xff0c;無任何相位或頻率關系&#xff08;如獨立晶振、不同時鐘樹&#xff09;工具完全禁用組間路徑的時序分析&#xff0c;但需用戶自行處理跨時鐘域&#xff08;CDC&a…

工作記錄 2017-01-06

工作記錄 2017-01-06 序號 工作 相關人員 1 協助BPO進行Billing的工作。 修改CSV、EDI837的導入。 修改郵件上的問題。 更新RD服務器。 郝 修改的問題&#xff1a; 1、 In “Full Job Summary” (patient info.), sometime, the Visit->Facility is missed, then …

Adaptive AUTOSAR UCM模塊——快速入門

Adaptive AUTOSAR中的UCM模塊介紹 概述 Adaptive AUTOSAR(AUTomotive Open System ARchitecture)是一個開放的行業標準,旨在為現代汽車電子系統提供一個靈活且可擴展的軟件框架。在這個框架中,更新與配置管理(Update and Configuration Management, UCM)模塊扮演著至關…

解決跨域問題的6種方案

解決跨域問題&#xff08;Cross-Origin Resource Sharing, CORS&#xff09;是 Web 開發中常見的需求&#xff0c;以下是 6 種主流解決方案&#xff0c;涵蓋前端、后端和服務器配置等不同層面&#xff1a; 一、CORS&#xff08;跨域資源共享&#xff09; 原理 通過服務器設置…

Python Selenium庫入門使用,圖文詳細。附網頁爬蟲、web自動化操作等實戰操作。

文章目錄 前言1 創建conda環境安裝Selenium庫2 瀏覽器驅動下載&#xff08;以Chrome和Edge為例&#xff09;3 基礎使用&#xff08;以Chrome為例演示&#xff09;3.1 與瀏覽器相關的操作3.1.1 打開/關閉瀏覽器3.1.2 訪問指定域名的網頁3.1.3 控制瀏覽器的窗口大小3.1.4 前進/后…

50個經典的python庫

本文整理了50個可以迅速掌握的經典Python庫&#xff0c;了解它們的用途&#xff0c;無論你是剛踏上編程之路&#xff0c;還是希望在Python的世界里更加深入&#xff0c;這50個庫都能幫助你快速起飛。 1. Taipy Taipy是一個開源Python庫&#xff0c;用于輕松的端到端應用程序開…

【視頻】V4L2、ffmpeg、OpenCV中對YUV的定義

1、常見的YUV格式 1.1 YUV420 每像素16位 IMC1:YYYYYYYY VV-- UU– IMC3:YYYYYYYY UU-- VV– 每像素12位 I420: YYYYYYYY UU VV =>YUV420P YV12: YYYYYYYY VV UU =>YUV420P NV12: YYYYYYYY UV UV =>YUV420SP(最受歡迎格式) NV21: YYYYYYYY VU VU =>YUV420SP…

freeswitch(多臺服務器級聯)

親測版本centos 7.9系統–》 freeswitch1.10.9本人freeswitch安裝路徑(根據自己的路徑進入)/usr/local/freeswitch/etc/freeswitch使用場景: 使用服務器級聯需要雙方網絡可以ping通,也就是類似局域網內,比如A服務器IP 192.168.1.100 B服務器 192.168.1.101,通過C設備注冊…

SpringMVC 基本概念與代碼示例

1. SpringMVC 簡介 SpringMVC 是 Spring 框架中的一個 Web 層框架&#xff0c;基于 MVC&#xff08;Model-View-Controller&#xff09; 設計模式&#xff0c;提供了清晰的分層結構&#xff0c;適用于 Web 應用開發 SpringMVC 主要組件 DispatcherServlet&#xff08;前端控…

LuaJIT 學習(1)—— LuaJIT介紹

文章目錄 介紹Extensions Modulesbit.* — Bitwise operationsffi.* — FFI libraryjit.* — JIT compiler controlC API extensionsProfiler Enhanced Standard Library Functionsxpcall(f, err [,args...]) passes arguments例子&#xff1a; xpcall 的使用 load*() handle U…

std::ranges::views::common, std::ranges::common_view

std::ranges::views::common, std::ranges::common_view C20 引入的用于將范圍適配為“通用范圍”的工具&#xff0c;主要解決某些算法需要傳統迭代器對&#xff08;如 begin 和 end 類型相同&#xff09;的問題。 基本概念 1. 功能 適配傳統算法&#xff1a;將范圍&#x…

4.3 數組和集合的初始及賦值

版權聲明&#xff1a;本文為博主原創文章&#xff0c;轉載請在顯著位置標明本文出處以及作者網名&#xff0c;未經作者允許不得用于商業目的 版權聲明&#xff1a;本文為博主原創文章&#xff0c;轉載請在顯著位置標明本文出處以及作者網名&#xff0c;未經作者允許不得用于商…

分布式光伏發電的發展現狀與前景

分布式光伏發電的發展現狀與前景 1、分布式光伏發電的背景2、分布式光伏發電的分類2.1、集中式光伏發電2.1.1、特點、原則2.1.2、優點2.1.3、缺點 2.2、分布式光伏發電2.2.1、特點、原則2.2.2、優點2.2.3、缺點 2.3、對比 3、分布式光伏發電的現狀4、分布式光伏發電的應用場景4…

13 | 實現統一的錯誤返回

提示&#xff1a; 所有體系課見專欄&#xff1a;Go 項目開發極速入門實戰課&#xff1b;歡迎加入 云原生 AI 實戰 星球&#xff0c;12 高質量體系課、20 高質量實戰項目助你在 AI 時代建立技術競爭力&#xff08;聚焦于 Go、云原生、AI Infra&#xff09;&#xff1b;本節課最終…