Linux--線程的控制

目錄

0.前言

?1.pthread庫

2.關于控制線程的接口

2.1.創建線程(pthread_create)

2.2.線程等待(pthread_join)

代碼示例1:

?編輯

***一些問題***

2.?3.創建多線程??

3.線程的終止?(pthread_exit? / pthread_cancel)

總結:

4.線程分離 (pthread_detach)

新線程分離主線程

5.C++ 11中的多線程


0.前言

線程的創建,終止,等待,分離


?1.pthread庫

????????Linux中有線程嗎?沒有,只有輕量級進程--(就是線程)。因此Linux下的系統調用只會給用戶提供創建輕量級進程的接口,這些接口需要被pthread庫進行封裝,按照線程的接口提供給用戶,用戶通過這些接口來創建,終止,等待,分離線程。所以我們稱Linux的線程為用戶級線程,windows的線程為內核級線程。

?


2.關于控制線程的接口

2.1.創建線程(pthread_create)

引入接口:pthread_create,用于創建一個新線程

參數說明

  • pthread_t *thread:這是一個指向?pthread_t?類型的指針,用于存儲新創建的線程的標識符。通過這個標識符,你可以引用或操作這個線程。
  • const pthread_attr_t *attr:這是一個指向?pthread_attr_t?類型的指針,用于設置線程的屬性,如堆棧大小、調度策略等。如果傳遞?NULL,則使用默認屬性。
  • void *(*start_routine) (void *):這是新線程將要執行的函數的指針。該函數必須接受一個?void *?類型的參數并返回一個?void *?類型的值。這個函數的參數?arg?將被傳遞給新線程。(輸入一個函數的地址)
  • void *arg:這是傳遞給?start_routine?函數的參數。

如果成功,pthread_create?返回?0;如果失敗,則返回錯誤碼。


2.2.線程等待(pthread_join)

引入接口:pthread_join

參數說明

  • pthread_t thread:這是要等待的線程的標識符(ID),該標識符是由?pthread_create?函數返回的。
  • void **retval:這是一個指向?void *?指針的指針,用于接收被等待線程的返回值。如果被等待的線程調用了?pthread_exit?并傳遞了一個返回值,或者簡單地返回了一個值(對于從?void*?返回類型的線程函數),那么這個值就可以通過這個參數返回給等待的線程。如果對這個返回值不感興趣,可以傳遞?NULL

如果成功,pthread_join?返回?0;如果失敗,則返回錯誤碼。


代碼示例1:

線程的創建和等待:

#include <iostream>
#include <string>
#include <pthread.h>
#include <unistd.h>void *threadrun(void *args)
{int cnt =10;while(cnt){std::cout<<"new thread run ...,cnt: "<<cnt--<<std::endl;sleep(1);}return nullptr;
}
int main()
{pthread_t tid;int n = pthread_create(&tid,nullptr,threadrun,(void*)"thread 1");std::cout<<"main thread join begin..."<<std::endl;n= pthread_join(tid,nullptr);if(n==0){std::cout<<"main thread wait success"<<std::endl;}return 0;
}

***一些問題***

問題1:mian和new線程誰先運行?不確定

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

join來保證,不join呢?會造成類似僵尸進程的問題

問題3:tid是什么樣子的?

代碼:以16進制的形式打印出來?

std::string PrintToHex(pthread_t &tid)
{char buffer[64];snprintf(buffer, sizeof(buffer), "0x%lx", tid);return buffer;
}std::string tid_str = PrintToHex(tid); // 我們按照16進行打印出來std::cout << "tid : " << tid_str << std::endl;

這個線程id是一個虛擬地址,后面再談


問題4:全面看待線程函數傳參,它可以傳任意類型,當然也可以傳類對象的地址,這意味著我們可以給線程傳遞多個參數,多種方法了

class ThreadData
{
public:std::string name;int num;
};
void *threadrun(void *args)
{//靜態強轉 ThreadData *td = static_cast<ThreadData*>(args);int cnt =10;while(cnt){std::cout << td->name << " run ..." <<"num is: "<<td->num<< ", cnt: " << cnt-- << std::endl;sleep(1);}return nullptr;
}
int main()
{ThreadData *td=new ThreadData();td->name ="thread-1";td->num = 1;int n = pthread_create(&tid,nullptr,threadrun,(void*)&td);
}

傳類對象的時候最好是在堆上開辟,這樣多個線程之間就不會互相干擾。


問題5:pthread_create第三個參數的返回值,該返回值是void*類型的,如果主線程想要獲取線程的返回值,可以通過join函數獲取(在線程沒出錯的情況下是能獲取到的,如果某一個線程出錯,主線程也是會跟著崩掉,因為線程出錯誤,是直接給整個進程發信號的,導致整個進程都掛掉了)

代碼示例:返回一個類對象

#include <iostream>
#include <string>
#include <thread>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>class ThreadData
{
public:int Excute(){return x + y;}
public:std::string name;int x;int y;// other
};class ThreadResult
{
public:std::string print(){return std::to_string(x) + "+" + std::to_string(y) + "=" + std::to_string(result);}
public:int x;int y;int result;
};
std::string PrintToHex(pthread_t &tid)
{char buffer[64];snprintf(buffer, sizeof(buffer), "0x%lx", tid);return buffer;
}
void *threadRun(void *args)
{ThreadData *td = static_cast<ThreadData*>(args); // (ThreadData*)argsThreadResult *result = new ThreadResult();int cnt = 10;while(cnt){sleep(3); std::cout << td->name << " run ..." << ", cnt: " << cnt-- << std::endl;result->result = td->Excute();result->x = td->x;result->y = td->y;break;//跑一次退出}delete td;return (void*)result;
}
int main()
{pthread_t tid;ThreadData *td=new ThreadData();td->name="thread-1";td->x=10;td->y=20;int n = pthread_create(&tid, nullptr, threadRun, td);std::string tid_str = PrintToHex(tid); // 我們按照16進行打印出來std::cout << "tid : " << tid_str << std::endl;std::cout<<"main thread join begin..."<<std::endl;ThreadResult *result = nullptr; // 開辟了空間的!!!n = pthread_join(tid, (void**)&result); if(n == 0){std::cout << "main thread wait success, new thread exit code: " << result->print() << std::endl;}sleep(10);return 0;
}


2.?3.創建多線程??

下面是一段示例:

初步:創建線程id和線程name,保存所有線程的id信息,最后主線程回收每個線程

#include <iostream>
#include <string>
#include <vector>
#include <thread>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>const int num = 10;
std::string PrintToHex(pthread_t &tid)
{char buffer[64];snprintf(buffer, sizeof(buffer), "0x%lx", tid);return buffer;
}void *threadrun(void *args)
{std::string name = static_cast<const char*>(args);while(true){std::cout << name << " is running" << std::endl;sleep(1);break;}return args;
}
int main()
{std::vector<pthread_t> tids;for(int i = 0; i < num; i++){// 1. 有線程的idpthread_t tid;// 2. 線程的名字char *name = new char[128];snprintf(name, 128, "thread-%d", i+1);pthread_create(&tid, nullptr, threadrun, /*線程的名字*/name);//3.保存所有線程idtids.push_back(tid);}for(auto tid:tids){void*name=nullptr;pthread_join(tid,&name);std::cout<<(const char*)name<<"quit..."<<std::endl;delete (const char*)name;}}
?

我們用vector儲存線程id集


3.線程的終止?(pthread_exit? / pthread_cancel)

????????對于新線程來說,線程終止,函數return;main函數結束,主線程結束,表示整個進程結束!

????????關于exit:專門用來終止進程的,不能用來終止線程!任意一個線程調用exit都表示進程終止!如果你想讓一個線程馬上終止,這里就要用到第三個接口:pthread_exit

參數:

  • retval:這是一個指向任意數據的指針,該數據將被線程的終止狀態所使用,并且可以被其他線程通過調用?pthread_join?來訪問。

當然你還可以使用接口:pthread_cancel取消一個線程

參數:

  • thread:要發送取消請求的線程標識符(pthread_t 類型)。

代碼示例:

#include <iostream>
#include <string>
#include <vector>
#include <thread>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>const int num = 10;
std::string PrintToHex(pthread_t &tid)
{char buffer[64];snprintf(buffer, sizeof(buffer), "0x%lx", tid);return buffer;
}void *threadrun(void *args)
{std::string name = static_cast<const char*>(args);while(true){std::cout << name << " is running" << std::endl;sleep(1);break;}//return args;pthread_exit(args);
}
int main()
{std::vector<pthread_t> tids;for(int i = 0; i < num; i++){// 1. 有線程的idpthread_t tid;// 2. 線程的名字char *name = new char[128];snprintf(name, 128, "thread-%d", i+1);pthread_create(&tid, nullptr, threadrun, /*線程的名字*/name);//3.保存所有線程idtids.push_back(tid);}for(auto tid:tids){void*name=nullptr;pthread_join(tid,&name);std::cout<<(const char*)name<<"quit..."<<std::endl;delete (const char*)name;}sleep(100);
}

在主線程未退出的情況下,其它線程成功退出了。


線程取消,退出結果為-1;?#define PTHREAD_CANCELED ((void *) -1)

#include <iostream>
#include <string>
#include <vector>
#include <thread>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>const int num = 10;
std::string PrintToHex(pthread_t &tid)
{char buffer[64];snprintf(buffer, sizeof(buffer), "0x%lx", tid);return buffer;
}void *threadrun(void *args)
{std::string name = static_cast<const char*>(args);while(true){std::cout << name << " is running" << std::endl;sleep(1);}
}
int main()
{std::vector<pthread_t> tids;for(int i = 0; i < num; i++){// 1. 有線程的idpthread_t tid;// 2. 線程的名字char *name = new char[128];snprintf(name, 128, "thread-%d", i+1);pthread_create(&tid, nullptr, threadrun, /*線程的名字*/name);//3.保存所有線程idtids.push_back(tid);}sleep(5);for(auto tid : tids){pthread_cancel(tid); // 取消std::cout << "cancel: " << PrintToHex(tid) << std::endl;void *result = nullptr; // 線程被取消線程的退出結果是:-1 #define PTHREAD_CANCELED ((void *) -1)pthread_join(tid, &result);std::cout << (long long int)result << " quit..." << std::endl;}sleep(100);
}


總結:

? ? 新線程如何終止?

? ? 1. 線程函數 return

? ? 2. pthread_exit

? ? 3. main thread call pthread_cancel, 新線程退出結果是-1


4.線程分離 (pthread_detach)

????????線程分離的是將線程與創建它的進程(或主線程)的終止狀態分離。當一個線程被分離后,它依然屬于進程內部,但它不再需要被其他線程顯式地等待(通過?pthread_join)來釋放其資源。當分離的線程終止時,它的所有資源會自動被釋放回系統,無需其他線程的干預。

參數

  • thread:要分離的線程的標識符(pthread_t 類型)。

返回值

  • 成功時返回 0。
  • 失敗時返回錯誤號。

????????一個線程被創建,默認是joinable,必須要被join的;如果一個線程被分離,線程的工作狀態分離狀態,不需要/不能被join的。

? ? ? ? 這里我們還需要借助一個接口:pthread_self,一調用就是獲取自己的線程id


新線程分離主線程

代碼示例:一旦分離主線程就不能等待了,如果等待會發生什么?這里我們看一下分離且join后,join的返回值

? ? ? ? 我們發現返回值為:22,這說明主線程以等待就直接出錯了。所以主線程無需等待,主線程可以做自己的事情了。如果在線程分離的情況下,且主線程沒有做等待,新線程出錯了,整個進程也是直接掛掉的,因為它還是在進程內部。

#include <iostream>
#include <string>
#include <vector>
#include <thread>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>const int num = 10;
std::string PrintToHex(pthread_t &tid)
{char buffer[64];snprintf(buffer, sizeof(buffer), "0x%lx", tid);return buffer;
}void *threadrun(void *args)
{pthread_detach(pthread_self());std::string name = static_cast<const char*>(args);while(true){std::cout << name << " is running" << std::endl;sleep(1);}pthread_exit(args);
}
int main()
{std::vector<pthread_t> tids;for(int i = 0; i < num; i++){// 1. 有線程的idpthread_t tid;// 2. 線程的名字char *name = new char[128];snprintf(name, 128, "thread-%d", i+1);pthread_create(&tid, nullptr, threadrun, /*線程的名字*/name);//3.保存所有線程idtids.push_back(tid);}sleep(5);for(auto tid : tids){std::cout << "cancel: " << PrintToHex(tid) << std::endl;void *result = nullptr; // 線程被取消線程的退出結果是:-1 #define PTHREAD_CANCELED ((void *) -1)int n = pthread_join(tid, &result);std::cout << (long long int)result << " quit...n :" <<n<< std::endl;}sleep(100);
}


5.C++ 11中的多線程

????????C++11在Linux中使用多線程,編譯時也是要鏈接pthread庫,因為C++11中的多線程本質,就是對原生線程庫接口的封裝!!!

#include <iostream>
#include <string>
#include <vector>
#include <thread>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>void threadrun(std::string name, int num)
{while(num){std::cout << name << " num : " << num<< std::endl;num--;sleep(1);}
}int main()
{std::string name = "thread-1";std::thread mythread(threadrun, std::move(name), 10);while(true){std::cout << "main thhread..." << std::endl;sleep(1);}mythread.join();return 0;
}

?

?

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

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

相關文章

給數組/對象添加一個(key-value)對象

需要將一個value值前面加上key值&#xff0c;放進數組/對象中 this.$set(res.data[0],type,1) this.$set( target, key, value ) target&#xff1a;要更改的數據源(可以是對象或者數組) key&#xff1a;要更改的具體數據 value &#xff1a;重新賦的值。 結果&#xff1a;…

文華財經盤立方多空變色波段趨勢線指標公式源碼

文華財經盤立方多空變色波段趨勢線指標公式源碼&#xff1a; N1:20; N2:ROUND(N1/2,1); N3:ROUND(SQRT(N1),1); N4:2*EMA2(C,N2)-EMA2(C,N1); 尊重市場:EMA2(N4,N3),COLORRED,LINETHICK2; 尊重市場1:IF(尊重市場<REF(尊重市場,1), 尊重市場,NULL),COLORGREEN,LINETHIC…

C++之List模擬實現

目錄 list的邏輯結構 構造函數 拷貝構造函數 賦值運算符重載 返回迭代器的初始位置 返回迭代器的最終位置 元素的插入 頭插 尾插 刪除元素 頭刪 尾刪 清空整個鏈表 析構函數 正向迭代器 反向迭代器 整體代碼 上期我們學寫了list的基本操作&#xff0c;本期我…

蘇東坡傳-讀書筆記十一

蘇東坡對寫作與風格所表示的意見最為清楚。他說做文章“大略如行云流水&#xff0c;初無定質&#xff0c;但常行于所當行&#xff0c;常止于所不可不止。文理自然&#xff0c;姿態橫生。孔子曰&#xff1a;‘言之不文&#xff0c;行而不遠。’又曰&#xff1a;‘辭達而已矣。’…

【cocos creator】2.4.x實現簡單3d功能,點擊選中,旋轉,材質修改,透明材質

demo下載:(待審核) https://download.csdn.net/download/K86338236/89527924 const {ccclass, property } = cc._decorator;const enum box_color {NORMAL = 0,DASHED_LINE = 1,//虛線TRANSLUCENT = 2,//半透明 }@ccclass export default class main extends cc.Component {…

STC32G/F/8H通用無刷電機驅動板

STC32G/F/8H通用無刷電機驅動板 &#x1f4cc;相關篇《低成本STC32G8K64驅動控制BLDC開源入門學習方案》 ?該驅動板是在上一版的基礎上改版而來。這里的STC32G/F/8H所指的是封裝型號為-LQFP48的STC32G8K64、STC32G12K128、STC32F12K54、STC8H8K64U。是一款兼容有感和無感設計的…

數據結構--樹和二叉樹的一些知識點總結

樹是n個結點的有限集&#xff0c;當n0時&#xff0c;稱為空樹。樹是一種遞歸的數據結構&#xff0c;樹作為一種邏輯結構同時也是一種分層的結構結點的深度是從根開始自頂向下累加&#xff1b;結點的高度是從葉結點自底向上累加由于樹中的分支是有向的&#xff0c;即從雙親指向孩…

【Java算法】二分查找 下

&#x1f525;個人主頁&#xff1a; 中草藥 &#x1f525;專欄&#xff1a;【算法工作坊】算法實戰揭秘 一.山脈數組的峰頂索引 題目鏈接&#xff1a;852.山脈數組的峰頂 ? 算法原理 這段代碼實現了一個查找山峰數組中峰值索引的算法。山峰數組是一個先遞增后遞減的數組&…

玩具營銷是如何拿捏成年人錢包?

好像現在的成年人逐漸熱衷于偏向年輕化&#xff0c;問問題會好奇“尊嘟假嘟”&#xff0c;飯量上的“兒童套餐”&#xff0c;娃娃機前排長隊......而最突出的莫過于各類各式的玩具不斷收割當代年輕人&#xff0c;除去常給大朋友們小朋友們送去玩具福利的“麥、肯”雙門&#xf…

激光干涉儀可以完成哪些測量:全面應用解析

在高端制造領域&#xff0c;精度是衡量產品質量的關鍵指標之一。激光干涉儀作為一項高精度測量技術&#xff0c;其應用廣泛&#xff0c;對于提升產品制造精度具有重要意義。 線性測量&#xff1a;精確定位的基礎 激光干涉儀采用邁克爾遜干涉原理&#xff0c;實現線性測量。該…

AlphaGo 的傳奇故事

文章目錄 一、說明二、AlphaGo 傳奇&#xff08;一&#xff09;&#xff1a;游戲規則三、AlphaGo 傳奇(二)&#xff1a;它是如何運作的&#xff1f;四、AlphaGo 傳奇&#xff08;三&#xff09;&#xff1a;史詩般的戰斗和人工智能的未來 一、說明 1997 年&#xff0c;IBM 的“…

卷積神經網絡之ResNet50遷移學習

數據準備 下載狗與狼分類數據集&#xff0c;數據來自ImageNet&#xff0c;每個分類有大約120張訓練圖像與30張驗證圖像。使用download接口下載數據集&#xff0c;并自動解壓到當前目錄。 全是小狗的圖片 另一邊全是狼的圖片 加載數據集 狼狗數據集提取自ImageNet分類數據集&a…

2-3個月的幼貓能吃主食凍干嗎?第一次吃哪款主食凍干比較好

2-3個月的幼貓能吃凍干嗎&#xff1f;一般來說&#xff0c;幼貓在2-3個月左右的離乳期就可以吃凍干了。需要注意的&#xff0c;一個是要認準主食凍干&#xff0c;零食凍干會讓貓貓從小就挑食&#xff0c;以后就更不好糾正了。而且離乳期的貓貓沒有了母乳的保護&#xff0c;免疫…

Open3D 點對面的ICP算法配準(精配準)

目錄 一、概述 1.1核心思想 1.2實現步驟 二、代碼實現 2.1關鍵函數 2.2完整代碼 三、實現效果 3.1原始點云 3.2配準后點云 3.3計算數據 一、概述 基于點對面的ICP&#xff08;Iterative Closest Point&#xff09;配準算法是ICP的一種變體&#xff0c;它通過最小化源…

【Ty CLI】一個開箱即用的前端腳手架

目錄 資源鏈接基礎命令模板創建命令幫助選擇模板開始創建開發模板 開發背景npm 發布流程問題記錄模板創建超時 更新日志 資源鏈接 文檔&#xff1a;https://ty.cli.vrteam.top/ 源碼&#xff1a;https://github.com/bosombaby/ty-cli 基礎命令 1. npm 全局安裝 npm i ty-cli…

Zabbix Sia Zabbix 邏輯漏洞(CVE-2022-23134)

前言 CVE-2022-23134是一個中等嚴重度的漏洞&#xff0c;影響Zabbix Web前端。這個漏洞允許未經身份驗證的用戶訪問setup.php文件的某些步驟&#xff0c;這些步驟通常只對超級管理員開放。利用這個漏洞&#xff0c;攻擊者可以通過跳過某些步驟來重新配置Zabbix前端&#xff0c…

gazebo仿真環境中加入livox mid360

https://github.com/Livox-SDK/livox_laser_simulation 功能包適用于ubuntu18.04 gazebo9的可以直接編譯運行。在ubutun20.04 的系統下gazebo是11版本,需要做一些修改 CMakeLists.txt文件中的 add_compile_options(-std=c++11) 改為 add_compile_options(-std=c++17)fatal er…

二一、搭建自已的語言大模型

1. 配置langchain環境 使用conda創建一個虛擬環境,基于 Python3.10,并在虛擬環境內安裝項目的依賴。注意,大模型對gpu有一定的要求,否則速度會奇慢無比。 conda create -n langchain python=3.10 conda env list conda activate langchain # 拉取倉庫 $ git clone ht…

Redis-Jedis連接池\RedisTemplate\StringRedisTemplate

Redis-Jedis連接池\RedisTemplate\StringRedisTemplate 1. Jedis連接池1.1 通過工具類1.1.1 連接池&#xff1a;JedisConnectionFactory&#xff1a;1.1.2 test&#xff1a;&#xff08;代碼其實只有連接池那里改變了&#xff09; 2. SpringDataRedis&#xff08;lettuce&#…

終于弄明白了什么是EI!

EI是Engineering Index的縮寫&#xff0c;中文意為“工程索引”&#xff0c;是由美國工程信息公司(Engineering Information, Inc.)編輯出版的著名檢索工具。它始創于1884年&#xff0c;擁有超過一個世紀的歷史&#xff0c;是全球工程界最權威的文獻檢索系統之一。EI雖然名為“…