深入理解Linux中的線程控制:多線程編程的實戰技巧

個人主頁:chian-ocean

文章專欄-Linux

前言:

POSIX線程(Pthreads) 是一種在 POSIX 標準下定義的線程庫,它為多線程編程提供了統一的接口,主要用于 UNIX 和類 UNIX 系統(如 Linux、MacOS 和 BSD 等)。POSIX 線程(Pthreads)允許程序在多個處理器上并行運行,從而提高應用程序的性能,尤其在多核處理器環境中。

在這里插入圖片描述

監控線程的bash

while :; do ps -aL | head -1 ; ps -aL | grep thread ; sleep 1;done

線程的控制

  • 與線程有關的函數構成了?個完整的系列,絕?多數函數的名字都是以pthread_打頭的
  • 要使?這些函數庫,要通過引?頭? pthread.h
  • 鏈接這些線程函數庫時要使?編譯器命令的“-lpthread”選項

線程的創建(pthread_create)

在這里插入圖片描述

#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine)(void *), void *arg);

參數解析:

  1. pthread_t *thread:指向 pthread_t 類型的變量,這個變量將存儲新線程的 ID。
  2. const pthread_attr_t *attr:指向 pthread_attr_t 類型的指針,它包含了新線程的屬性(如棧大小、調度策略等)。如果傳入 NULL,則使用默認屬性。
  3. void *(*start_routine)(void *):這是一個指向線程執行函數的指針,該函數接收一個 void* 類型的參數,并返回 void* 類型的結果。
  4. void *arg:這是傳遞給 start_routine 函數的參數,允許你向線程傳遞數據。

示例:

#include<iostream>  
#include<unistd.h>     
#include<pthread.h>    using namespace std;    // 線程函數
void* mythread(void* args)
{// 創建一個循環,子線程會打印 6 次for(int i = 0; i < 6; i++) {sleep(1);                // 讓線程睡眠 1 秒鐘,模擬任務的執行cout << "Child Thread" << endl;  // 打印“Child Thread”,表示子線程在運行}return nullptr;
}// 主函數
int main()
{pthread_t tid;   // 定義一個線程ID變量// 創建子線程,線程的執行函數是 mythread,其他參數為默認值pthread_create(&tid, nullptr, mythread, nullptr);// 主線程執行,循環 6 次for(int i = 0; i < 6; i++) {cout << "Main Thread" << endl; // 打印“Main Thread”,表示主線程在運行sleep(1);                // 讓主線程睡眠 1 秒鐘}// 等待子線程完成執行pthread_join(tid, nullptr);  // 阻塞等待 tid 線程執行完畢return 0;  // 程序正常退出
}
  • pthread_create():用于創建一個新的線程,mythread 是新線程的執行函數。該函數接受參數 nullptr,表示沒有傳遞任何參數給線程。
  • pthread_join(tid, nullptr):等待線程 tid 執行完成,pthread_join 阻塞主線程,直到子線程執行完畢。

打印結果:

在這里插入圖片描述

線程退出

pthread_join

在這里插入圖片描述

#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
參數說明:
  • pthread_t thread:要等待結束的線程 ID。
  • void **retval:用于存儲線程返回值的指針。如果不需要返回值,可以將其設置為 NULL
功能描述:
  • pthread_join() 函數使得調用該函數的線程(通常是主線程)阻塞,直到指定的線程(由 thread 參數指定)執行完畢。
  • 當線程結束后,系統會回收該線程的資源。如果 retval 不為 NULL,線程的返回值將被存儲在 retval 指向的位置。
示例:
#include<iostream>
#include<unistd.h>
#include<pthread.h>using namespace std;// 子線程的執行函數
void* mythread(void*args)
{// 循環6次,每次打印一次信息,并且每次暫停1秒for(int i = 0 ; i < 6 ;i++) {sleep(1);  // 休眠1秒鐘cout << "Child Thread is running ...." << endl;  // 輸出子線程運行的提示}// 返回一個值100,并強制轉換為 void* 類型return (void*)100;
}int main()
{pthread_t tid;  // 定義一個線程ID變量// 創建子線程,傳入 mythread 函數作為線程執行的函數,參數為 nullptrpthread_create(&tid, nullptr, mythread, nullptr);// 主線程運行6次,每次輸出一次信息,并且每次暫停1秒for(int i = 0 ; i < 6 ;i++) {cout << "Main Thread is running .." << endl;  // 輸出主線程運行的提示sleep(1);  // 休眠1秒鐘}void* retval;  // 聲明一個指向 void 的指針,用于接收子線程的返回值// 等待子線程結束,并將子線程的返回值存儲到 retval 中pthread_join(tid, &retval);// 輸出子線程的返回值,將其強制轉換為整數類型并輸出cout << (int64_t)retval << endl;  // 輸出子線程的返回值,轉換為整數return 0;
}
程序流程:
  1. 并發輸出:主線程和子線程的輸出交替進行,打印在終端上時會交替顯示 “Main Thread is running …” 和 “Child Thread is running …”。
  2. 同步:主線程通過 pthread_join() 等待子線程完成,并獲取子線程的返回值。
  3. 線程返回值:子線程通過 return (void*)100 返回一個 void* 類型的值,主線程通過 pthread_join() 捕獲該返回值并輸出。

打印:

在這里插入圖片描述

pthread_exit

在這里插入圖片描述

#include <pthread.h>
void pthread_exit(void *retval);
參數說明
  • retval:這是一個指針,線程退出時可以返回的值。該值可以被其他線程通過 pthread_join 獲取,用來傳遞線程的退出狀態或其他信息。
  • 注意:retval 可以是任何類型的指針,通常是一個線程退出的狀態信息。
示例:
#include<iostream>
#include<unistd.h>
#include<pthread.h>using namespace std;void* mythread(void* args)  // 線程函數
{// 子線程執行的操作,循環輸出 "Child Thread is running ...."for (int i = 0; i < 6; i++) {sleep(1);  // 每次休眠1秒cout << "Child Thread is running ...." << endl;}// 輸出結束前的信息cout << "Child  Pthread_exit" << endl;// 使用 pthread_exit 顯式退出線程并返回一個狀態碼 100pthread_exit((void*)100);// 這一行代碼不會被執行到,因為 pthread_exit 已經退出線程return (void*)100;
}int main() 
{pthread_t tid;  // 聲明一個線程標識符// 創建線程pthread_create(&tid, nullptr, mythread, nullptr);// 主線程執行的操作for (int i = 0; i < 6; i++) {cout << "Main Thread is running .." << endl;sleep(1);  // 每次休眠1秒}// 主線程休眠,確保子線程有時間執行sleep(8);  // 等待子線程執行完畢// 主線程結束前輸出信息cout << "main return " << endl;return 0;  // 程序結束
}
程序流程
  • 程序運行時,子線程和主線程會交替輸出 “Child Thread is running …” 和 “Main Thread is running …”。
  • 主線程執行完成它的循環后,調用 sleep(8) 來等待子線程的執行。此時,子線程已經執行完它的循環,并通過 pthread_exit 顯式退出。
  • 由于沒有在主線程中調用 pthread_join(),主線程并沒有等待子線程完成,它只是通過 sleep(8) 暫停一段時間,確保子線程有足夠的時間結束。

打印:

在這里插入圖片描述

pthread_cancal

在這里插入圖片描述

#include <pthread.h>
int pthread_cancel(pthread_t thread);
參數說明
  • pthread_t thread:目標線程的線程 ID(TID),即你希望取消的線程。
功能描述
  • pthread_cancel 用于向指定的線程發送取消請求。調用此函數后,目標線程會接收到取消請求,并在合適的時機響應取消請求。
返回值
  • 返回 0 表示成功,其他返回值表示失敗,通常是因為無法取消線程(如線程已經結束等)。
示例:
#include<iostream>
#include<unistd.h>
#include<pthread.h>
#include<vector>
#include<string>
using namespace std;// Hex函數,用于將整數轉換為十六進制字符串
string Hex(int data)
{char buff[1034] = {0};// snprintf用于將整數data轉換為十六進制字符串snprintf(buff,sizeof(buff),"0x%x",data);return buff;
}// 線程函數,打印每個線程的整數值
void* mythread(void* args)
{// 將傳入的參數指針轉換為整型指針int* i = (int*)args;while(true) // 無限循環,模擬線程的持續運行{sleep(1);  // 每次休眠1秒cout << "thread: " << *i  << endl;  // 輸出線程ID(即傳遞給線程的值)}return (void*)100;  // 返回一個指針類型的值,通常線程退出時返回狀態
}int main()
{// 定義一個 vector 來存儲線程IDvector<pthread_t> th;// 創建4個線程for(int i = 0; i < 4; i++){pthread_t tid;  // 定義一個線程ID變量// 創建線程,將線程ID、線程函數(mythread)以及傳遞給線程的參數(i的地址)傳入pthread_create(&tid, nullptr, mythread, &i);// 將線程ID添加到線程容器中th.push_back(tid);}cout << "ready calcel" << endl;sleep(3);  // 休眠3秒,等待線程輸出// 取消每個創建的線程for(int i = 0; i < th.size(); i++){cout <<"calcel: thread "  << i <<endl;  // 輸出正在取消的線程編號pthread_cancel(th[i]);  // 取消對應的線程}sleep(1);  // 稍等1秒,確保線程能夠響應取消請求cout << "main return "<<endl;  // 輸出主線程返回信息return 0;  // 程序結束
}
程序流程
  • ** 主線程創建子線程:**主線程使用 pthread_create() 創建 4 個子線程,每個子線程打印傳遞給它的整數值(即循環中的 i),并在每秒打印一次。

  • **線程輸出:**每個線程在無限循環中每秒輸出 thread: <value>,其中 <value> 是它收到的整數(傳遞給線程的 i)。

  • **主線程等待:**主線程在創建完線程后,休眠 3 秒,允許線程輸出信息。

  • **取消線程:**主線程在 3 秒后調用 pthread_cancel() 來取消所有 4 個線程,每個線程被取消后,它會終止執行(線程的退出取決于它們在什么地方被取消)。

  • **程序結束:**主線程輸出 "main return" 并結束,程序執行完畢。

打印:

在這里插入圖片描述

線程分離

在這里插入圖片描述

#include <pthread.h>
int pthread_detach(pthread_t thread);

參數

  • thread:要設置為分離狀態的線程 ID。

功能描述

  • 功能描述pthread_detach 用于將一個線程設置為 分離狀態。當線程被設置為分離狀態后,線程在完成執行時會自動釋放資源,而不需要其他線程顯式地調用 pthread_join() 來清理線程資源。

示例:

#include<iostream>
#include<unistd.h>
#include<pthread.h>
#include<vector>
#include<string>
using namespace std;// Hex函數:將整數轉換為十六進制字符串
string Hex(int data)
{char buff[1034] = {0};snprintf(buff, sizeof(buff), "0x%x", data);  // 將整數以十六進制格式轉換成字符串return buff;
}// 線程函數
void* mythread(void* args)
{pthread_detach(pthread_self());  // 將當前線程設置為分離狀態int* i = (int*)args;  // 將傳入的參數指針轉換為整型指針int n = 3;// 循環打印線程編號(傳遞給線程的值),共打印3次while(n--){sleep(1);  // 每次循環休眠1秒cout << "thread: " << *i << endl;  // 輸出線程的編號}return (void*)100;  // 線程返回值
}int main()
{vector<pthread_t> th;  // 用于存儲線程ID的容器cout << "線程的創建" << endl;// 創建4個線程for(int i = 0; i < 4; i++){pthread_t tid;pthread_create(&tid, nullptr, mythread, &i);  // 創建線程并傳遞 i 的地址作為參數th.push_back(tid);  // 將創建的線程ID加入到線程容器中}sleep(10);  // 主線程休眠10秒,確保所有子線程能運行完cout << "main return " << endl;return 0;  // 主程序結束
}

程序執行流程:

  1. 主線程通過 pthread_create 創建 4 個子線程,每個子線程都會執行 mythread 函數。
  2. 每個子線程會打印 3 次其編號(線程的參數),并在每次打印后休眠 1 秒。
  3. 主線程休眠 10 秒,以確保子線程有足夠的時間打印信息。
  4. 每個子線程在完成執行后會自動退出并釋放資源,因為它們是分離線程(調用了 pthread_detach)。
  5. 主線程輸出 “main return” 并結束。

打印:

在這里插入圖片描述

  • 以上的代碼沒有實現同步,可能會導致錯亂打印。

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

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

相關文章

(mac)Grafana監控系統之監控Linux的Redis

Grafana安裝-CSDN博客 普羅米修斯Prometheus監控安裝&#xff08;mac&#xff09;-CSDN博客 1.Redis_exporter安裝 直接下載 wget https://github.com/oliver006/redis_exporter/releases/download/v1.0.3/redis_exporter-v1.0.3.linux-amd64.tar.gz 解壓 tar -xvf redis_…

鴻蒙應用元服務開發-Account Kit未成年人模式訂閱和處理用戶信息變更

一、概述 通過訂閱用戶信息變更&#xff0c;您可以接收有關用戶及其賬戶的重要更新。當用戶取消元服務的授權信息、注銷華為賬號時&#xff0c;華為賬號服務器會發送通知到元服務&#xff0c;元服務可以根據通知消息進行自身業務處理。 二、用戶信息變更事件介紹 三、訂閱用…

buildroot構建根文件系統報錯(已解決大部分問題)

title: buildroot構建根文件系統報錯(set FORCE_UNSAFE_CONFIGURE1) author: cbus categories: 小知識 tags:小知識 abbrlink: 53691 date: 2025-04-20 08:03:00 錯誤1 set FORCE_UNSAFE_CONFIGURE1 在使用buildroot構建根文件系統時&#xff0c;一切按照文檔的配置&#xff0…

7.QT-常用控件-QWidget|font|toolTip|focusPolicy|styleSheet(C++)

font API說明font()獲取當前widget的字體信息.返回QFont對象.setFont(const QFont& font)設置當前widget的字體信息. 屬性說明family字體家族.?如"楷體",“宋體”,"微軟雅?"等.pointSize字體??weight字體粗細.以數值?式表?粗細程度取值范圍為[…

通過面向目標的獎勵彌合人與機器人的靈活性差距

24年10月來自紐約大學的論文“Bridging the Human to Robot Dexterity Gap through Object-Oriented Rewards”。 直接通過人類視頻訓練機器人是機器人技術和計算機視覺領域的一個新興領域。盡管雙指機械手在雙指夾持器方面取得了顯著進展&#xff0c;但以這種方式讓多指機械手…

C++入門篇(下)

目錄 1、引用 1.1 引用概念 1.2 引用特性 1.3 常引用 1.4 使用場景 1.4.1 引用做參數 1.4.2 引用做返回值 1.5 引用和指針的區別 2、內聯函數 2.1 概念 2.2 特性 3、auto關鍵字 4、基于范圍的for循環 5、指針空值nullptr 5.1 C98 中的指針空值處理 5.2 C11 …

Multi-Query Attention (MQA) PyTorch 實現

和多頭注意力機制的唯一區別&#xff1a;K、V在不同的head之間實現了復用&#xff0c;而對于不同的頭&#xff0c;Q依然不同。 因此這里的代碼和標準多頭注意力的實現也是幾乎完全一樣&#xff1a; import torch import torch.nn as nn import torch.nn.functional as Fclass…

visual studio無法跳轉到函數定義、變量定義、跳轉函數位置不準問題解決

參考&#xff1a;https://blog.csdn.net/snakehacker/article/details/135438353 程序有時會出現大部分函數都不能準確的從頭文件中正確定位到函數定位,這是因為數據庫錯亂造成的,可以通過重構數據庫來解決,操作方法如下&#xff1a; 菜單欄&#xff1a;工具——選項 文本編輯…

Java優雅實現判空方法

在 Java 開發中&#xff0c;頻繁的 if (obj ! null) 判空代碼會導致代碼冗余、可讀性差&#xff0c;且容易遺漏判空導致 NullPointerException。以下從 語言特性、設計模式、工具類 和 編碼規范 四個維度&#xff0c;結合實際案例&#xff0c;詳解如何優雅處理空值問題。 一、…

京東百億補貼殺入外賣市場:一場關乎即時零售未來的攻防戰

當美團和餓了么在外賣市場雙雄爭霸十余年之際&#xff0c;京東突然以"百億補貼免傭金"的組合拳高調入場。這場看似跨界的外賣大戰&#xff0c;實則是互聯網巨頭對萬億級即時零售市場的生死爭奪。 外賣只是表象&#xff0c;即時零售才是終極戰場 京東黑板報4月10日官…

UNION和UNION ALL的主要區別

UNION和UNION ALL的主要區別在于處理重復數據和排序的方式。 UNION和UNION ALL都是SQL語言中用于合并兩個或多個SELECT語句結果集的關鍵字。它們的主要區別如下&#xff1a; 1、對重復結果的處理&#xff1a;UNION在進行表鏈接后會篩選掉重復的記錄&#xff0c;而UNION ALL不會…

七段碼 路徑壓縮 并查集 dfs

12.七段碼 - 藍橋云課 將七個二極管映射為 1-7 開一個二維矩陣 為 相鄰的邊連上線 edge[1][2] edge[1][6] 1;edge[2][1] edge[2][3] edge[2][7] 1;edge[3][2] edge[3][4] edge[3][7] 1;edge[4][3] edge[4][5] 1;edge[5][4] edge[5][6] edge[5][7] 1;edge[6][1…

科技如何改變世界?

技術是我們日常生活中不可或缺的一部分&#xff0c;以至于我們常常忘記了它的重要性。如果你正在科技領域工作&#xff0c;或者希望進入該領域&#xff0c;你可能是眾多有使命感的人之一&#xff0c;希望知道自己的日常工作能為社會或地球的長遠利益做出貢獻。 別再四處尋找了…

抽象的https原理簡介

前言 小明和小美是一對好朋友&#xff0c;他們分隔兩地&#xff0c;平時經常寫信溝通&#xff0c;但是偶然被小明發現他回給小美的信好像被人拆開看過&#xff0c;甚至偷偷被篡改過。 對稱加密算法 開頭的通信過程比較像HTTP服務器與客戶端的通信過程&#xff0c;全明文傳輸…

高級java每日一道面試題-2025年4月13日-微服務篇[Nacos篇]-Nacos如何處理網絡分區情況下的服務可用性問題?

如果有遺漏,評論區告訴我進行補充 面試官: Nacos如何處理網絡分區情況下的服務可用性問題&#xff1f; 我回答: 在討論 Nacos 如何處理網絡分區情況下的服務可用性問題時&#xff0c;我們需要深入理解 CAP 理論以及 Nacos 在這方面的設計選擇。Nacos 允許用戶根據具體的應用…

python解壓文件 zip tar.gz tar.xz

以下代碼為解壓zip包 tar包文件 zip_path&#xff1a;文件絕對路徑 output_folder&#xff1a;文件解壓后存放的文件夾路徑 def extract_file(zip_path, output_folder):# 支持解壓zip tar tar.gz tar.xz .tar.bz2# 確保輸出文件夾存在os.makedirs(output_folder, exist_okT…

網絡基礎(協議,地址,OSI模型、Socket編程......)

目錄 一、計算機網絡發展 二、協議 1.認識協議 2.OSI七層模型 3.TCP/IP 五層(或四層)模型 4.協議本質 三、網絡傳輸流程 1.MAC地址 2.協議棧 3.IP地址 IP地址 vs MAC地址 1. 核心區別 2. 具體通信過程類比 3. 關鍵總結 為什么需要兩者&#xff1f; 4.協議棧圖解…

生成式AI對話中提示詞策略:明確問題、明確目標和提供背景信息是最有效的策略

生成式AI對話中提示詞策略:明確問題、明確目標和提供背景信息是最有效的策略 最有效的提示詞策略包括明確問題、明確目標和提供背景信息。普適性有效提示詞策略可分為三類:明確需求與精確指引型、清晰解釋與邏輯排序型、拆解任務與多樣化表達型。[局限]數據來源于中國用戶,…

AtCoder ABC402 ABCD

A - CBC 把大寫字母按順序連起來 B - Restaurant Queue 一眼隊列&#xff0c;stl模擬就行 C - Dislike Foods 顯然&#xff0c;每次克服暴力枚舉每個菜肴會超時。 然而題目中給了每個菜肴的配菜個數&#xff0c;不妨換過來統計每個配菜用在了哪些菜肴。每次克服時&#x…

Transformer 架構 - 解碼器 (Transformer Architecture - Decoder)

歡迎回到我們的 Transformer 系列教程!在上一篇中,我們詳細探討了 Transformer 的編碼器,它負責將輸入的源序列(比如源語言句子)轉換為一系列包含豐富上下文信息的向量表示。 現在,我們將把目光投向 Transformer 的另一半——解碼器 (Decoder)。解碼器負責接收編碼器的輸…