linux|多線程(一)

主要介紹了為什么要有線程 和線程的調用 和簡單的對線程進行封裝。

背景知識

a.重談地址空間

我們知道物理內存的最小單元大小是4kB
物理內存是4G那么這樣的單元友1M個
操作系統先描述再組織struct page[1M]
對于32位數據字長的機器,頁表有2^32條也就是4G條,每一條光保存兩個地址加一個標記位 這個頁表就得有36G這樣大
實際上比如 0010 0010 0010 0010 0010 0010 0010 0010
在這里插入圖片描述
虛擬地址>> 12 得到 頁框號
虛擬地址&&oxfff 得到 頁內偏移量

線程的概念

線程:在進程內部運行,是cpu調度的基本單位
進程承擔系統資源的基本實體
在這里插入圖片描述
進程承擔系統資源的基本實體 怎么理解這句話呢?可以把進程看作家庭,國家的最小單位國是千萬家~~家是社會資源分配的最小單位,把線程看作個人,線程是進程的一部分

os要單獨設計線程 --新建?暫停?銷毀?調度?線程要不要和進程產生關聯

在windows 有 專門管理線程的數據結構

struct tcb
{
// 線程id
// 線程 優先級
// 狀態上下文
// 鏈接屬性
};

在這里插入圖片描述
但是linux 中沒有專門的線程,Linux 是用進程模擬的線程
linux中的執行流,我們稱為輕量級進程
線程:在進程內部運行,是cpu調度的基本單位
在cpu看來被調用的都是輕量級進程

澄清進程和線程

進程 是 只有一個執行流的線程~

見一見線程

在這里插入圖片描述
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);

pthread_t *thread: 一個指向pthread類型的指針 , pthread 這個類型用于保存的線程的標識符
const pthread_attr_t *attr: 線程屬性對象的引用,可以用來設置線程的優先級、調度策略等。如果不需要設置特殊屬性,可以傳入 NULL 使用默認屬性。
start_routine: 指向線程開始執行的函數的指針這個函數的類型應為 void ()(void*),意味著它接受一個 void * 類型的參數,并返回 void * 類型的結果。
arg: 傳遞給 start_routine 函數的參數,類型為 void *。這使得你可以在創建線程時向線程函數傳遞數據。

#include <iostream>
#include <unistd.h>void *threadStart(void * args)
{while(true){std::cout<<"new thraed running..."<<",pid:"<<getpid()<<std::endl;sleep(1);}
}int main()
{pthread_t tid;// 新線程pthread_create(&tid, nullptr, threadStart,(void *) "thread-new"); // 相當于把頁表分成了兩部分// 主線程while(true){sleep(1);std::cout<<"main thraed running..."<<",pid:"<<getpid()<<std::endl;}return 0;
}

有了多進程為什么還要有多線程

線程不用再創建頁表等,只需要一個pcb 創建的成本低
運行期間線程,的調度成本低,因為不需要切換頁表
刪除線程,比刪除一個進程的成本低,只需要刪除一個pcb就行了

線程的調度成本為什么更低呢?

我們切換頁表只需要修改對應寄存器中的值就可以了,這個不是主要原因
最主要的原因是
在這里插入圖片描述
數據會被緩存在cache中,一切換進程,cache中的數據就作廢了,就需要重新cache了

為什么還要有進程呢?

線程的健壯性比較低,一個線程出錯整個程序都退出了

哪些東西在多線程中是共享的

因為同一地址空間,所以代碼段,數據段都是共享的,文件描述符表,每種信號的處理方式,工作目錄。

哪些東西是各自都有一份的呢?

線程id,錯誤碼,調度優先級,調度的上下文,信號屏蔽字
**最重要的:**一組寄存器,保存硬件上下文數據 線程是在調度運行的
棧:線程的棧主要用于存儲函數調用的局部變量、函數參數以及返回地址。每當線程調用一個新的函數時,相關的局部變量和函數參數就會被壓入該線程的棧中,形成一個新的棧幀。當函數返回時,相應的棧幀就會被彈出,恢復之前的調用環境。由于每個線程有獨立的棧,它們可以并發地進行函數調用,而不會相互干擾。

幾個基本的問題

問題1:主線程和新線程誰先運行?

系統會將這個新線程加入到就緒隊列中等待調度。調度器會根據其自身的算法(比如時間片輪轉、優先級等)來決定哪個線程獲得 CPU 時間片并開始執行。

問題2:我們期望誰最后退出? main thread,你如何保證呢?

我們希望主線程后退出,如果主線程先退出,主線程退出了整個進程就結束了,可能此時新線程還沒有完成任務呢。
我們用pthread_join 來保證 主線程后退出

void * RunThread(void * args)
{std:: string name;name = (const char *)args;std::cout <<name << std::endl;return args;
}
int main()
{pthread_t tid;pthread_create(&tid, nullptr, RunThread,(void *)"thread - 1");int n = pthread_join(tid,nullptr);if(n == 0){std::cout<<"wait success"<<std::endl;}return 0;
}

在這里插入圖片描述

問題3: tid 是什么樣子的?是什么呢?虛擬地址! 為什么?

void  PrintToHEX(pthread_t tid)
{char message[1024];snprintf(message,sizeof(message),"0x%lx",tid);std::cout<<message<<std::endl;
}

我們打出來發現是一個地址
在這里插入圖片描述
為什么是一個地址呢?我們等下再說

問題4:全面看待線程函數傳參: 我們可以傳遞任意類型,但你一定要能想得起來,也可以傳遞類對象的地址!!!

class ThreadData
{
public:int x;int y;
};
void * RunThread(void * args)
{std:: string name;ThreadData * data = (ThreadData *)args;std::cout <<"x:"<<data->x <<" y:"<<data->y<< std::endl;return args;
}
void  PrintToHEX(pthread_t tid)
{char message[1024];snprintf(message,sizeof(message),"0x%lx",tid);std::cout<<message<<std::endl;
}
int main()
{pthread_t tid1;ThreadData x1;x1.x = 10;x1.y =20;pthread_create(&tid1, nullptr, RunThread,(void *)&x1);// PrintToHEX(tid);int n = pthread_join(tid1,nullptr);if(n == 0){std::cout<<"wait success"<<std::endl;}return 0;
}

問題6:注意剛剛我們x1的寫法其實是不標準的 為什么?

#include <iostream>
#include <pthread.h>
#include <string>
class ThreadData
{
public:int x;int y;
};
void * RunThread1(void * args)
{std:: string name;ThreadData * data = (ThreadData *)args;data->x = 5;data -> y = 10;std::cout <<"x:"<<data->x <<" y:"<<data->y<< std::endl;return args;
}
void * RunThread(void * args)
{std:: string name;ThreadData * data = (ThreadData *)args;std::cout <<"x:"<<data->x <<" y:"<<data->y<< std::endl;return args;
}
void  PrintToHEX(pthread_t tid)
{char message[1024];snprintf(message,sizeof(message),"0x%lx",tid);std::cout<<message<<std::endl;
}
int main()
{pthread_t tid1;ThreadData x1;x1.x = 10;x1.y =20;pthread_create(&tid1, nullptr, RunThread,(void *)&x1);pthread_t tid2;pthread_create(&tid2, nullptr, RunThread1,(void *)&x1);int n = pthread_join(tid1,nullptr);if(n == 0){std::cout<<"wait success"<<std::endl;}return 0;
}

比如說這樣,我們原本想要打印 10 ,20 5,10 的 結果卻這樣。
在這里插入圖片描述
因為main 棧上的變量 這兩個線程都可以看到并修改,且這個變量只有一份,當第一個線程正準備打印的時候,第二個線程修改了這兩個變量的值。導致第一個線程打印出來就和第二個線程一樣了。 根本原因就是 變量只有一份,我們多創建一份就好了。

而且我們不推薦直接 將main棧上的變量給新線程因為 main函數棧的變量生命周期是隨main函數的,可能新線程早都結束了,但是main棧上的變量就是不結束
我們用new 可以在新線程中方便管理 傳過來的變量的生命周期。

問題5: 全面看待線程函數返回

它也可以傳遞自定義類型哦

#include <iostream>
#include <pthread.h>
#include <string>
class ThreadData
{
public:int x;int y;int Excute(){return x + y; }
};
class ThreadResult
{
public:int x;int y;int ans;void print(){std::cout<<std::to_string(x)+"+"+std::to_string(y)+"="+std::to_string(ans)<<std::endl;}
};void * RunThread(void * args)
{std:: string name;ThreadData * data = (ThreadData *)args;//std::cout <<"x:"<<data->x <<" y:"<<data->y<< std::endl;ThreadResult * result = new ThreadResult();result->ans = data->Excute();result->x = data->x;result->y = data->y;return (void*)result;
}
void  PrintToHEX(pthread_t tid)
{char message[1024];snprintf(message,sizeof(message),"0x%lx",tid);std::cout<<message<<std::endl;
}
int main()
{pthread_t tid1;ThreadData * x1 = new ThreadData();x1->x = 10;x1->y = 20;pthread_create(&tid1, nullptr, RunThread,(void *)x1);ThreadResult * result =nullptr;int n = pthread_join(tid1,(void**)&result);result->print();if(n == 0){std::cout<<"wait success"<<std::endl;}return 0;
}

問題6: 如何創建多線程呢?

我們通過循環的方式 創建多線程哦~~

#include <iostream>
#include <pthread.h>
#include <string>
#include <unistd.h>
void * RunThread(void * args)
{std::string name = (const char *) args;std::cout<<name<<std::endl;return args;
}
int main()
{for(int i = 0; i < 10; i++){pthread_t tid;char name[1024];snprintf(name,sizeof(name),"this is %d new thread",i+1);pthread_create(&tid, nullptr , RunThread,name);}sleep(100);return 0;
}

在這里插入圖片描述
我們發現結果并不符合預期。 因為name 只有一份,主線程 運行的時候不停在覆蓋 name。
我們用 new ,循環 new 十次 主線程分配的時候每一個線程都得到 一個地址,那個地址指向一個獨立的堆空間,線程之間就不互相影響了。
在這里插入圖片描述
ok 完美解決~~!!!

問題7: 新線程如何終止?

1.我們可以在新線程中用return
2.我們可以在新線程中用pthread_exit 注意exit 是終止整個進程。
3.我們可以在新線程中使用pthrad_cancel
線程被取消線程的退出結果是:-1 #define PTHREAD_CANCELED ((void *) -1)

#include <iostream>
#include <pthread.h>
#include <string>
#include <unistd.h>
#include <vector>void *RunThread(void *args)
{while (true){std::string name = (const char *)args;std::cout << name << std::endl;sleep(3);}return args;
}int main()
{std::vector<pthread_t> tids;for (int i = 0; i < 10; i++){pthread_t tid;// char name[1024];char *name = new char[1024];snprintf(name, 1024, "this is %d new thread", i + 1);pthread_create(&tid, nullptr, RunThread, name);tids.push_back(tid);}for (auto tid : tids){// pthread_detach(tid);pthread_cancel(tid);void *ret = nullptr;int n = pthread_join(tid, &ret);std::cout << (long long int)ret << " quit.." << std::endl;// std::cout<<"n="<<n<<std::endl;}return 0;
}

在這里插入圖片描述

pthread_cancel 是請求取消另一個線程,而 pthread_exit 是線程自身決定退出。

在這里插入圖片描述
在這里插入圖片描述
某個線程調用了exit 整個進程就結束了~
exit 你走錯片場了啊。要用pthread_exit

void * RunThread(void * args)
{std::string name = (const char *) args;std::cout<<name<<std::endl;pthread_exit(args);return args;
}

在這里插入圖片描述

問題8: 可以不可以不join線程,讓他執行完就退出呢??可以!

我們用pthread_detach 就可以了,然后線程變成unjoinable 狀態,如果此時再等待就會出錯 然后終止整個進程。

#include <iostream>
#include <pthread.h>
#include <string>
#include <unistd.h>
#include <vector>void *RunThread(void *args)
{while (true){std::string name = (const char *)args;std::cout << name << std::endl;sleep(3);}return args;
}int main()
{std::vector<pthread_t> tids;for (int i = 0; i < 10; i++){pthread_t tid;// char name[1024];char *name = new char[1024];snprintf(name, 1024, "this is %d new thread", i + 1);pthread_create(&tid, nullptr, RunThread, name);tids.push_back(tid);}for (auto tid : tids){pthread_detach(tid); // 主線程分離新線程,新線程必須存在}for (auto tid : tids){// pthread_detach(tid);pthread_cancel(tid);void *ret = nullptr;int n = pthread_join(tid, &ret);std::cout << "n:"<<n<<std::endl;// std::cout<<"n="<<n<<std::endl;}return 0;
}

在這里插入圖片描述

封裝線程

#include <string>
#include <pthread.h>
#include <functional>
namespace ThreadModel
{   class Thread{typedef void  (*func_t)(std::string); // ?// using func_t = std::function<void()>;public:Thread(const std::string & name,func_t func):_name(name),_func(func){}~Thread(){}void Excute(){_isrunning = true;_func(_name); // 這里傳了線程名_isrunning = false;}static void * Routine(void * args) // 不用static 第一個參數是this{Thread * self  = static_cast<Thread*>(args);//  self->_func();self->Excute();return nullptr;}void start(){pthread_create(&_tid,nullptr,Routine,(void *)this);}void join(){// std::cout<<_isrunning<<std::endl;//  std::cout<<true<<std::endl;if(_isrunning) // 只有在運行中的線程才需要被等待{//std::cout<<"..."<<std::endl;pthread_join(_tid,nullptr);}}// 終止線程void stop(){if(_isrunning){_isrunning = false;pthread_cancel(_tid);}}private:std::string _name;pthread_t _tid;func_t _func; bool _isrunning;  // 主要 用于線程終止時 只有啟動了的線程 才能終止      };
}

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

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

相關文章

嵌入式linux相機 轉換模塊

convert_manager.c #include <config.h> #include <convert_manager.h> #include <string.h>static PT_VideoConvert g_ptVideoConvertHead NULL;/*********************************************************************** 函數名稱&#xff1a; Register…

異常檢測在機器學習中的重要性

異常檢測在機器學習中的重要性 在機器學習領域&#xff0c;異常檢測&#xff08;Anomaly Detection&#xff09;是一種識別數據集中異常或不尋常模式的任務。這些異常點可能代表錯誤、噪聲、或更有趣的是&#xff0c;它們可能揭示了某些異常行為或新現象。異常檢測在許多領域都…

LabVIEW Communications LTE Application Framework 讀書筆記

目錄 硬件要求一臺設備2臺USRPUSRP-2974 示例項目的組件文件夾結構DL Host.gcompeNodeB Host.gcompUE Host.gcompBuildsCommonUSRP RIOLTE 操作模式DLeNodeBUE 項目組件單機雙機UDP readUDP writeMAC TXMAC RXDL TX PHYDL RX PHYUL TX PHYUL RX PHYSINR calculationRate adapta…

Vue + litegraph.js 實現藍圖功能

Vue3 litegraph.js 實現藍圖功能 litegraph.js [github](https://github.com/jagenjo/litegraph.js) [demo](https://tamats.com/projects/litegraph/editor/)vue - html <canvas id"mycanvas" width"1524" height"720" style"border…

PostgreSQL數據庫從入門到精通系列之九:PostgreSQL數據庫13版本和PostgreSQL數據庫14版本功能特性

PostgreSQL數據庫從入門到精通系列之九:PostgreSQL數據庫13版本和PostgreSQL數據庫14版本功能特性 一、PostgreSQL數據庫版本13新功能和特性二、PostgreSQL13相比于PostgreSQL12功能改進三、PostgreSQL數據庫14版本新功能和特性一、PostgreSQL數據庫版本13新功能和特性 Postg…

R語言優雅的把數據基線表(表一)導出到word

基線表&#xff08;Baseline Table&#xff09;是醫學研究中常用的一種數據表格&#xff0c;用于在研究開始時呈現參與者的初始特征和狀態。這些特征通常包括人口統計學數據、健康狀況和疾病史、臨床指標、實驗室檢測、生活方式、社會經濟等。 本人在既往文章《scitb包1.6版本發…

無人機之機型區別與應用領域

一、多旋翼無人機 特點&#xff1a;多旋翼無人機依靠產生升力以平衡飛行器的重力&#xff0c;通過改變每個旋翼的轉速來控制飛行姿態&#xff0c;能夠懸停和垂直起降。他們具備體積小、重量輕、噪音小、隱蔽性好的特點&#xff0c;操作靈活且易于維護。 應用&#xff1a;多旋…

Springboot Excel 導出工具 -- EasyPoi 簡介

EasyPoi是一款基于 Apache POI 的高效 Java 工具庫&#xff0c;專為簡化 Excel 和 Word 文檔的操作而設計。以下是對 EasyPoi 的詳細介紹&#xff1a; 一、概述 名稱&#xff1a;EasyPoi類型&#xff1a;Java 庫功能&#xff1a;簡化 Excel 和 Word 文檔的操作&#xff0c;包…

uni-app:文字豎直排列,并且在父級view中水平豎直對齊

一、效果 二、代碼 <template><view class"parent"><text class"child">這是豎直排列的文字</text></view> </template> <script>export default {data() {return {}},methods: {},}; </script> <sty…

Vue 實現文章錨點定位,頂欄遮住了錨點,使用scrollTo代替scrollIntoView設置偏移量

在Vue中實現文章錨點功能&#xff0c;可以通過監聽滾動事件來更新當前錨點的狀態。以下是一個簡單的示例&#xff1a; <template><div><div :id"anchor- index" v-for"(section, index) in sections" :key"index">{{ sectio…

React和Vue.js的相似性和差異性是什么?

React 和 Vue.js 都是流行的前端 JavaScript 框架&#xff0c;它們有一些相似性和差異性&#xff1a; 相似性&#xff1a; 組件化&#xff1a;React 和 Vue.js 都支持組件化開發&#xff0c;允許開發者將界面拆分為獨立的組件&#xff0c;提高代碼的復用性和可維護性。…

學習小記-Kafka相較于其他MQ有啥優勢?

Kafka 相比于 RocketMQ 有以下幾個優勢&#xff1a; 1. 高吞吐量和低延遲&#xff1a; Kafka 以其出色的 I/O 性能和分布式架構設計&#xff0c;能夠實現極高的吞吐量&#xff0c;每秒數百萬的消息處理能力&#xff0c;適合大規模數據流處理。同時&#xff0c;Kafka 設計為…

pycharm2020 相比pycarm2017更新內容

PyCharm 是 JetBrains 開發的一款流行的 Python 集成開發環境&#xff08;IDE&#xff09;。從 2017 版到 2020 版&#xff0c;PyCharm 進行了多個版本的更新&#xff0c;添加了許多新功能和改進。以下是一些主要的更新內容和改進&#xff1a; PyCharm 2018 和 2019 的主要更新…

vault安裝手冊

標準配置文件 ui true cluster_addr "https://127.0.0.1:8201" api_addr "https://127.0.0.1:8200" disable_mlock truestorage "raft" {path "/path/to/raft/data"node_id "raft_node_id" }listen…

Ubuntu 24.04安裝Jellyfin媒體服務器圖解教程

使用 Jellyfin 等開源軟件創建媒體服務器肯定能幫助您管理和跨各種設備傳輸媒體集合。當你有一個封閉社區時&#xff0c;這尤其有用。 什么是 Jellyfin 媒體服務器&#xff1f; Jellyfin 媒體服務器&#xff0c;顧名思義&#xff0c;是一款開源軟件&#xff0c;允許用戶使用本…

網絡抓包工具tcpdump的使用

tcpdump tcpdump命令是基于unix系統的命令行的數據報嗅探工具&#xff0c;可以抓取流動在網卡上的數據包&#xff0c;熟悉 tcpdump 的使用能夠幫助你分析調試網絡數據。 原理 linux抓包是通過注冊一種虛擬的底層網絡協議來完成對網絡報文&#xff08;準確的是網絡設備&#xf…

鼠標的發明和鼠標“變形記”

注&#xff1a;機翻&#xff0c;未校對。 Who Invented the Computer Mouse? 誰發明了電腦鼠標&#xff1f; It was technology visionary and inventor Douglas Engelbart (January 30, 1925 – July 2, 2013) who revolutionized the way computers worked, turning it fr…

Flink源碼學習資料

Flink系列文檔腦圖 由于源碼分析系列文檔較多&#xff0c;本人繪制了Flink文檔腦圖。和下面的文檔目錄對應。各位讀者可以選擇自己感興趣的模塊閱讀并參與討論。 此腦圖不定期更新中…… 文章目錄 以下是本人Flink 源碼分析系列文檔目錄&#xff0c;歡迎大家查閱和參與討論。…

偽元素::before :: after的用法?

::before 和 ::after 是 CSS 偽元素&#xff0c;用于在元素內容的前面或后面插入內容。這些偽元素不會改變文檔的實際內容&#xff0c;但可以用來添加裝飾性元素或文本。以下是它們的用法和一些常見示例。 基本用法 ::before ::before 偽元素用于在元素的內容之前插入內容。 …

【簡潔明了】調節大模型的prompt的方法【帶案例】

簡明調節大模型的prompt的方法【簡潔明了帶案例】 1. 明確任務目標2. 提供上下文3. 指定格式4. 限制輸出長度5. 使用示例6. 逐步引導7. 提供反面例子8. 使用CoT思維鏈9. 反復試驗和調整方法九解釋&#xff1a;喬哈里窗檢視 最后 因為網上給出的調節prompt都 過于詳細&#xff…