Linux:41線程控制lesson29

1.線程的優點:

? 創建?個新線程的代價要?創建?個新進程?得多

創建好線程只要調度就好了

? 與進程之間的切換相?,線程之間的切換需要操作系統做的?作要少很多

為什么?

? 最主要的區別是線程的切換虛擬內存空間依然是相同的,但是進程切換是不同的。這兩種上 下?切換的處理都是通過操作系統內核來完成的。

內核的這種切換過程伴隨的最顯著的性能 損耗是將寄存器中的內容切換出。

? 另外?個隱藏的損耗是上下?的切換會擾亂處理器的緩存機制。簡單的說,?旦去切換上下 ?,處理器中所有已經緩存的內存地址?瞬間都作廢了。還有?個顯著的區別是當你改變虛 擬內存空間的時候,處理的?表緩沖 TLB (快表)會被全部刷新,這將導致內存的訪問在? 段時間內相當的低效。但是在線程的切換中,不會出現這個問題,當然還有硬件cache。

? 線程占?的資源要?進程少

? 能充分利?多處理器的可并?數量

線程是調度的基本單位

? 在等待慢速I/O操作結束的同時,程序可執?其他的計算任務

多進程也可以做,也是進程的優點

? 計算密集型應?,為了能在多處理器系統上運?,將計算分解到多個線程中實

加密,解密,壓縮。

通俗解釋:計算機多個CPU,把計算任務拆分成多份,分別跑

最好只創建跟CPU對等的線程

? I/O密集型應?,為了提?性能,將I/O操作重疊。線程可以同時等待不同的I/O操作。

線程可以多創建。概率上總會有線程讀數據,執行。

線程切換?

(1)進程切換:PCB切成另一個進程的PCB。

task_struct*current:表示OS全局指針,current指向當前進程,寄存器。換跟換。

(2)線程切換:?
成本比進程低為什么?

同一個進程的線程切換:
不用保存CR3寄存器。------>看不出來誰的成本更低
頁表不用切換。
?

為什么進程切換成本高???

進程切換的兩個大緩存:

(1)對用戶數據進行cache:
cache緩存:
Cache(緩存)是計算機系統中的一種高速存儲器,位于CPU和主內存之間,用于暫時存儲CPU可能頻繁訪問的數據和指令。Cache的主要作用是減少CPU訪問主內存的次數,從而提高系統的整體性能。
Cache的訪問速度非常快,通常在幾個時鐘周期內就可以完成數據的讀取或寫入。通過將CPU頻繁訪問的數據和指令存儲在Cache中,CPU可以直接從Cache中讀取數據,從而大大減少了訪問主內存的次數,提高了系統性能

(2)TLB快表

總結:

進化切換,會導致TLB和Cache失效,下次運行,需要重新緩存

線程不切換頁表,不會出現緩存失效,所以線程切換成本更低。

2.線程的缺點?

? 性能損失

? ?個很少被外部事件阻塞的計算密集型線程往往?法與其它線程共享同?個處理器。如果計 算密集型線程的數量?可?的處理器多,那么可能會有較?的性能損失,這?的性能損失指 的是增加了額外的同步和調度開銷,?可?的資源不變。

? 健壯性降低

? 編寫多線程需要更全?更深?的考慮,在?個多線程程序?,因時間分配上的細微偏差或者 因共享了不該共享的變量?造成不良影響的可能性是很?的,換句話說線程之間是缺乏保護 的。

? 缺乏訪問控制

? 進程是訪問控制的基本粒度,在?個線程中調?某些OS函數會對整個進程造成影響。

缺乏訪問控制,也是優點,可以自由地訪問資源

? 編程難度提?

? 編寫與調試?個多線程程序?單線程程序困難得多

3.線程異常

? 單個線程如果出現除零,野指針問題導致線程崩潰,進程也會隨著崩潰

? 線程是進程的執?分?,線程出異常,就類似進程出異常,進?觸發信號機制,終?進程,進程 終?,該進程內的所有線程也就隨即退出

Linux進程VS線程

進程和線程

? 進程是資源分配的基本單位

? 線程是調度的基本單位

? 線程共享進程數據,但也擁有??的?部分“私有”數據::

? 線程ID

? ?組寄存器,線程的上下文

“線程要有自己的獨立上下文數據 - >線程是可以被獨立調度的。”

? 棧

線程都有自己獨立的棧結構->線程是一個動態的概念(“有生命周期”)。

? errno “錯誤碼”

? 信號屏蔽字

? 調度優先級

進程大部分資源獨占,少量資源共享
線程相反。

進程被多個線程共享

同?地址空間,因此Text Segment、Data Segment都是共享的,如果定義?個函數,在各線程中都可以調 ?,如果定義?個全局變量,在各線程中都可以訪問到,除此之外,各線程還共享以下進程資源和環境:

? ?件描述符表

? 每種信號的處理?式(SIG_IGN、SIG_DFL或者?定義的信號處理函數)

? 當前?作?錄

? ??id和組id

進程和線程的關系如下圖:

關于進程線程的問題

? 如何看待之前學習的單進程?具有?個線程執?流的進程

?Linux線程控制

????????創建一個線程

功能:創建?個新的線程 原型:

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void * (*start_routine)(void*), void *arg);

(1)參數: thread:返回線程ID attr:設置線程的屬性,attr為NULL表?使?默認屬性

(2)start_routine:是個函數地址,線程啟動后要執?的函數

(3)arg:傳給線程啟動函數的參數

(4)返回值:成功返回0;失敗返回錯誤碼

代碼?

Makefile
test_thread:TestThread.ccg++ -o $@ $^ 
.PHONY:clean
clean:rm -f test_thread
?TestThread.cc:自己寫的√
#include<iostream>
#include<pthread.h>
#include<string>
#include<unistd.h>using namespace std;void* threadstat(void*args){string name = (const char*)args;while(true){cout<<"我是子進程,name:"<<name<<endl;sleep(1);}return nullptr;
}int main()
{pthread_t tid;pthread_create(&tid,nullptr,threadstat,(void*)"thread-1");while(true){cout<<"我是主線程"<<endl;sleep(10);}
}

?編譯不通過

不是系統調用

test_thread:TestThread.ccg++ -o $@ $^ -lpthread
.PHONY:clean
clean:rm -f test_thread

需要加上庫pthread,-l:引入庫文件的操作

編譯通過:結果,沒有子進程,兩個死循環跑起來

?只有一個進程,殺掉,兩個都沒了。

理解?

ps -aL:查看線程

創建線程成功:
新線程執行:threadrun,新線程入口
編譯出來就是一組虛擬地址

main函數,就是另一組虛擬地址,表示代碼和數據。

pid:是一樣的,屬于同一個進程
TTY:終端一樣
LWP-light weight process:輕量級進程
“主進程,pid和lwp一樣”
“線程pid和主進程一樣,lwp與pid不一樣”

task_strut里面有兩個數據

pid_t pid;
pid_t lwp;

問題?

1.關于調度的時間片問題:時間片是等分給不同的線程。
2.線程異常??

代碼:√
#include<iostream>
#include<pthread.h>
#include<string>
#include<unistd.h>using namespace std;void* threadstat(void*args){string name = (const char*)args;while(true){cout<<"我是子進程,name:"<<name<<endl;sleep(1);int a = 1;a/=0;}return nullptr;
}int main()
{pthread_t tid;pthread_create(&tid,nullptr,threadstat,(void*)"thread-1");while(true){cout<<"我是主線程"<<endl;sleep(10);}
}

任何一個線程崩潰,都會導致整個進程崩潰

3.消息混在一起?
顯示器是共享資源

引入pthread庫?

為什么會有一個庫?這個庫是什么東西?

c++11多線程demo

c++11也引入了多線程叫做
thread


?

#include <iostream>
#include <string> 
#include <thread>void hello(){while (true){std::cout << "新線程: hello world, pid: " << getpid() << std::endl;sleep(1);}}int main(){std::thread t(hello);while (true){std::cout << "我是主線程..." << ", pid: " << getpid() << std::endl;sleep(1);}t.join();return 0;}

報錯,由于沒有使用原生線程庫pthread.?

test_thread:TestThread.ccg++ -o $@ $^ -std=c++11 -
.PHONY:clean
clean:rm -f test_thread

這樣就OK

test_thread:TestThread.ccg++ -o $@ $^ -std=c++11 -lpthread
.PHONY:clean
clean:rm -f test_thread

?c++11的多線程,在linux下,本質就是堆pthread的封裝

?

c++,在不同的環境下,對線程進程不同的封裝。?

所有語言的線程都要獨立進行封裝

?線程控制的接口

創建線程:pthread_create

功能:創建?個新的線程 原型:

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void * (*start_routine)(void*), void *arg);

(1)參數: thread:返回線程ID attr:設置線程的屬性,attr為NULL表?使?默認屬性

(2)start_routine:是個函數地址,線程啟動后要執?的函數

(3)arg:傳給線程啟動函數的參數

(4)返回值:成功返回0;失敗返回錯誤碼

?終止線程:pthread_exit | pthred_cancle

如果需要只終?某個線程?不終?整個進程,可以有三種?法:

pthread_exit函數:
功能:線程終?
原型:void pthread_exit(void *value_ptr);參數:value_ptr:value_ptr不要指向?個局部變量。
返回值:?返回值,跟進程?樣,線程結束的時候?法返回到它的調?者(??)

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

pthread_cancel函數
功能:取消?個執?中的線程 原型: int pthread_cancel(pthread_t thread);參數: thread:線程ID返回值:成功返回0;失敗返回錯誤碼
線程等待:pthread_join

為什么需要線程等待?

? 已經退出的線程,其空間沒有被釋放,仍然在進程的地址空間內。

? 創建新的線程不會復?剛才退出線程的地址空間。

?線程創建好之后,新線程要被主線程等待

---->不然會觸發,類似僵尸進程,內存泄漏

功能:等待線程結束
原型int pthread_join(pthread_t thread, void **value_ptr);參數:thread:線程IDvalue_ptr:它指向?個指針,后者指向線程的返回值返回值:成功返回0;失敗返回錯誤碼

調?該函數的線程將掛起等待,直到id為thread的線程終?。thread線程以不同的?法終?,通過 pthread_join得到的終?狀態是不同的,總結如下:

1. 如果thread線程通過return返回,value_ptr所指向的單元?存放的是thread線程函數的返回值。

2. 如果thread線程被別的線程調?pthread_cancel異常終掉,value_ptr所指向的單元?存放的是常 數PTHREAD_CANCELED

3. 如果thread線程是??調?pthread_exit終?的,value_ptr所指向的單元存放的是傳給 pthread_exit的參數。

4. 如果對thread線程的終?狀態不感興趣,可以傳NULL給value_ptr參數。

#include<iostream>
#include<pthread.h>
#include<string>
#include<unistd.h>
#include<cstdio>using namespace std;int flag = 100;
void showid(pthread_t &tid)//減少拷貝
{printf("tid: 0x%lx",tid);
}string Formatid(const pthread_t &tid){char id[64];snprintf(id,sizeof(id),"0x%lx",tid);return id;
}void* threadstat(void*args){string name = (const char*)args;pthread_t id = pthread_self();int cnt = 5;while(cnt){cout<<"我是子進程,name:"<<name<<"我的Id是"<<Formatid(id)<<endl;;sleep(1);cnt--;flag++;}return nullptr;
}int main()
{pthread_t tid;pthread_create(&tid,nullptr,threadstat,(void*)"thread-1");showid(tid);int cnt = 6;while(cnt--){cout<<"我是主線程"<<"我的Id是"<<Formatid(pthread_self())<<"flag:"<<flag<<endl;sleep(1);}void *ret = nullptr; // ret也是一個變量!!也有空間哦!// 等待的目標線程,如果異常了,整個進程都退出了,包括main線程,所以,join異常,沒有意義,看也看不到!// jion都是基于:線程健康跑完的情況,不需要處理異常信號,異常信號,是進程要處理的話題!!!pthread_join(tid, &ret); // 為什么在join的時候,沒有見到異常相關的字段呢??std::cout << "ret is : " << (long long int)ret << std::endl;return 0;
}

返回值存放在ret里面,可以查看,長整型。

返回線程的tid:pthread_self

誰調用,就獲取誰的tid

std::string FormatId(const pthread_t &tid)
{char id[64];snprintf(id, sizeof(id), "0x%lx", tid);return id;
}

?線程傳參和返回值

主線程在調用FormatId,
新線程也會調用。
該函數被稱為可重入函數?

?代碼1:驗證join可以去的線程執行完后的退出碼/返回值

#include<iostream>
#include<unistd.h>
#include<pthread.h>
#include<string>
using namespace std;void* routine(void* arg){string name = static_cast<const char*>(arg);int cnt = 5;while(cnt--){cout<<"我是子線程,名字:"<<name<<endl;sleep(1);}return (void*)10;
}int main(){pthread_t tid;pthread_create(&tid,nullptr,routine,(void*)"thread-1");int cnt = 5;while(cnt--){cout<<"我是主線程"<<endl;sleep(1);}void* ret = nullptr;pthread_join(tid,&ret);cout<<"子進程退出,退出碼為:"<<(long long)ret<<endl;
}

1.main函數結束,代表主線程結束,也代表進程結束
2.新線程對應的入口函數,運行結束,代表當前線程運行結束。
3.問題:給線程傳遞的參數和返回值,可以是任意類型?

代碼2:30min證明::給線程傳遞的參數和返回值,可以是任意類型 ,下面的例子是類類型

#include<iostream>
#include<unistd.h>
#include<pthread.h>
#include<string>
using namespace std;class Task{public:Task(int a,int b):_a(a),_b(b){}int Cal(){return _a+_b;}~Task() {}private:int _a;int _b;
};
class Result{public:Result(int result):_result(result){}int getresult(){return _result;}~Result(){}private:int _result;
};
void* routine(void* arg){Task*t = static_cast<Task*>(arg);sleep(1);Result* r = new Result(t->Cal());sleep(1);return r;
}int main(){pthread_t tid;Task* t = new Task(20,10);pthread_create(&tid,nullptr,routine,t);Result* ret = nullptr;pthread_join(tid,(void**)&ret);int n = ret->getresult();cout<<"子線程的返回值是:"<<n<<endl;
}

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

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

相關文章

【MCP】從一個天氣查詢服務帶你了解MCP

1. 前言 這篇文章將通過一個集成高德天氣查詢的 MCP Server 用例&#xff0c;帶你上手開發自己的 MCP Server ,文章將通過以下三種方式&#xff08;自己編寫 Client 端代碼&#xff0c;使用 mcp-cli 自帶頁面&#xff0c;集成到 Claude 桌面版等&#xff09;帶你測試自己的 MC…

SHCTF-REVERSE

前言 之前寫的&#xff0c;一直沒發&#xff0c;留個記錄吧&#xff0c;萬一哪天記錄掉了起碼在csdn有個念想 1.ezapk 反編譯 快速定位關鍵函數 package com.mycheck.ezjv;import adrt.ADRTLogCatReader; import android.app.Activity; import android.content.Context; impo…

安卓觸摸事件分發機制分析

1. 前言 &#x1f3af; 一句話總結&#xff1a; 觸摸事件&#xff08;TouchEvent&#xff09;會從 Activity 層開始&#xff0c;按從外到內的方式傳遞給每一個 ViewGroup/View&#xff0c;直到某個 View 消費&#xff08;consume&#xff09; 它&#xff0c;事件傳遞就會停止…

Spring MVC 多個攔截器的執行順序

一、流程總覽 該流程圖描述了一個多層攔截器鏈的業務處理流程&#xff0c;核心邏輯為&#xff1a; 前置攔截&#xff1a;通過 predHandler1 和 predHandler2 逐層校驗請求合法性。核心處理&#xff1a;通過校驗后執行核心業務邏輯 handler()。后置處理與清理&#xff1a;按反…

django filter 排除字段

在Django中&#xff0c;當你使用filter查詢集&#xff08;QuerySet&#xff09;時&#xff0c;通常你會根據模型的字段來過濾數據。但是&#xff0c;有時你可能想要排除某些特定的字段&#xff0c;而不是過濾這些字段。這里有幾種方法可以實現這一點&#xff1a; 使用exclude方…

ByeCode,AI無代碼開發平臺,拖拽式操作構建應用

ByeCode是什么 ByeCode 是一款先進的 AI 無代碼平臺&#xff0c;旨在幫助企業迅速創建數字名片、網站、小程序、應用程序及內部管理系統&#xff0c;無需繁雜的編碼或開發工作。ByeCode 采用直觀的可視化界面和拖拽式操作&#xff0c;使得非技術用戶能夠輕松上手。同時&#x…

AI日報 - 2025年04月28日

&#x1f31f; 今日概覽(60秒速覽) ▎&#x1f916; 能力進展 | Gemini 2.5 Pro成功挑戰《口袋妖怪紅》8道館&#xff1b;AI推理器具備自我糾錯能力&#xff1b;LLM在游戲、多模態理解、代碼遷移等方面展現新能力。 ▎&#x1f4bc; 商業動向 | Google回應DOJ反壟斷案&#xff…

在Java中實現List按自定義順序排序的幾種方案

在Java中實現List按自定義順序排序的幾種方案 在實際開發中&#xff0c;我們經常需要對集合中的對象按照特定字段進行排序。當排序規則不是簡單的字母或數字順序&#xff0c;而是自定義的順序時&#xff0c;我們需要采用特殊的方法。本文將以一個List<Person>按省份特定…

微服務架構在云原生后端的深度融合與實踐路徑

??個人主頁??:一ge科研小菜雞-CSDN博客 ????期待您的關注 ???? 一、引言:后端架構的演變,走向云原生與微服務融合 過去十余年,后端架構經歷了從單體應用(Monolithic)、垂直切分(Modularization)、到微服務(Microservices)的演進,每一次變化都是為了解決…

Python中的Walrus運算符分析

Python中的Walrus運算符&#xff08;:&#xff09;是Python 3.8引入的一個新特性&#xff0c;允許在表達式中同時賦值和返回值。它的核心作用是減少重復計算&#xff0c;提升代碼簡潔性。以下是其適用的典型場景及示例&#xff1a; 1. 在循環中避免重復計算 當循環條件需要多次…

用Node.js施展文檔比對魔法:輕松實現Word文檔差異比較小工具,實現Word差異高亮標注(附完整實戰代碼)

引言&#xff1a;當「找不同」遇上程序員的智慧 你是否經歷過這樣的場景&#xff1f; 法務同事發來合同第8版修改版&#xff0c;卻說不清改了哪里 導師在論文修改稿里標注了十幾處調整&#xff0c;需要逐一核對 團隊協作文檔頻繁更新&#xff0c;版本差異讓人眼花繚亂 傳統…

前端瀏覽器窗口交互完全指南:從基礎操作到高級控制

瀏覽器窗口交互是前端開發中構建復雜Web應用的核心能力&#xff0c;本文深入探討23種關鍵交互技術&#xff0c;涵蓋從傳統API到最新的W3C提案&#xff0c;助您掌握跨窗口、跨標簽頁的完整控制方案。 一、基礎窗口操作體系 1.1 窗口創建與控制 // 新窗口創建&#xff08;現代瀏…

Git和Gitlab的部署和操作

一。GIT的基本操作 1.GIT的操作和查看內容 [rootmaster ~]# yum install git -y [rootmaster ~]# git config --list&#xff1a;查看所有配置 2.GIT倉庫初始化 [rootmaster ~]# mkdir /gittest&#xff1a;創建目錄 [rootmaster ~]# cd /gittest/&#xff1a;進入目錄 [rootm…

Linux中線程池的簡單實現 -- 線程安全的日志模塊,策略模式,線程池的封裝設計,單例模式,餓漢式單例模式,懶漢式單例模式

目錄 1. 對線程池的理解 1.1 基本概念 1.2 工作原理 1.3 線程池的優點 2. 日志與策略模式 2.1 日志認識 2.2 策略模式 2.2.1 策略模式的概念 2.2.2 工作原理 2.2 自定義日志系統的實現 3. 線程池設計 3.1 簡單線程池的設計 3.2 線程安全的單例模式線程池的設計 3…

量子力學:量子通信

量子通信是利用量子力學原理對信息進行編碼、傳輸和處理的新型通信方式&#xff0c;以下是其詳細介紹及業界發展現狀&#xff1a; 基本原理 量子疊加態 &#xff1a;量子系統可以處于多個狀態的疊加&#xff0c;如光子的偏振方向可以同時處于水平和垂直方向的疊加態&#xff…

企業架構之旅(1):TOGAF 基礎入門

大家好&#xff0c;我是沛哥兒。今天我們簡單聊下TOGAF哈。 文章目錄 一、TOGAF 是什么定義與核心定位發展歷程與行業地位與其他架構框架的區別 二、TOGAF 核心價值企業數字化轉型助力業務與 IT 的協同作用降本增效與風險管控 三、TOGAF 基礎術語解析架構域&#xff08;業務、…

CSS 內容超出顯示省略號

CSS 內容超出顯示省略號 文章目錄 CSS 內容超出顯示省略號**1. 單行文本省略&#xff08;常用&#xff09;****2. 多行文本省略&#xff08;如 2 行&#xff09;****3. 對非塊級元素生效****完整示例****注意事項** 在 CSS 中實現內容超出顯示省略號&#xff0c;主要通過控制文…

路由器重分發(OSPF+RIP),RIP充當翻譯官,OSPF充當翻譯官

路由器重分發&#xff08;OSPFRIP&#xff09; 版本 1 RIP充當翻譯官 OSPF路由器只會OSPF語言&#xff1b;RIP路由器充當翻譯官就要會OSPF語言和RIP語言&#xff1b;則在RIP中還需要將OSPF翻譯成RIPOSPF 把RIP路由器當成翻譯官&#xff0c;OSPF路由器就只需要宣告自己的ip&am…

AlexNet網絡搭建

AlexNet網絡模型搭建 環境準備 首先在某個盤符下創建一個文件夾&#xff0c;就叫AlexNet吧&#xff0c;用來存放源代碼。 然后新建一個python文件&#xff0c;就叫plot.py吧&#xff0c;往里面寫入以下代碼&#xff0c;用于下載數據集&#xff1a; # FashionMNIST里面包含了…

【計算機網絡】網絡基礎概念

&#x1f4da; 博主的專欄 &#x1f427; Linux | &#x1f5a5;? C | &#x1f4ca; 數據結構 | &#x1f4a1;C 算法 | &#x1f152; C 語言 | &#x1f310; 計算機網絡 這是博主計算機網絡的第一篇文章&#xff0c;本文由于是基礎概念了解&#xff0c;引用了大…