Linux(線程控制)

一 線程的操作

1. 創建線程:pthread_create

int pthread_create(pthread_t *thread,                 // 線程 idconst pthread_attr_t *attr,        // 線程屬性設置void *(*start_routine) (void *),   // 回調函數void *arg                          // 傳遞給回調函數的參數);
// 返回值0成功,否則返回錯誤碼

2. 等待線程:pthread_join

int pthread_join(pthread_t thread,  // pthread_create 的返回值 void **retval      // pthread_create 回調函數的返回值);
// 返回值成功0,否則返回錯誤碼

和進程一樣,線程執行完,也需要等待回收獲取執行結果,否則類似僵尸進程。

示例:

#include <iostream>
#include <pthread.h>void* fun(void *arg)
{const char *s = static_cast<const char *>(arg);std::cout << s << std::endl;return (void *)"正常退出";
}
int main()
{pthread_t pid;pthread_create(&pid, nullptr, fun, (void *)"hello world");void *result;pthread_join(pid, &result);std::cout << static_cast<const char *>(result) << std::endl;return 0;
}

創建多線程,進程內部就有多個執行流,誰先執行不一定。

void* 可以接收任意類型參數,內置類型,自定義類型都可以。

二級指針存放回調函數的返回值(一級指針的地址)

3. 線程終止

#include <pthread.h>void pthread_exit(void *retval  // 終止后的信息);
// 哪個線程調用終止哪個線程

如果不想正常return返回,可以調用 pthread_exit() ,提前終止,攜帶退出信息。

4. 線程分離

#include <pthread.h>int pthread_detach(pthread_t thread  // 線程的PID);
// 讓這個線程分離,此后不需要join

一般情況線程結束需要被等待,但可以自己分離出進程,但資源仍然共享,只是由系統來回收。

5. 取消線程

int pthread_cancel(pthread_t thread // 線程ID)
// 取消線程

可以主動取消一個線程,一般是由主線程來取消。

6. 封裝原生API(簡易)

#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <functional>// 處理任務
using handler = std::function<void(void)>;class mythread
{
public:// 線程回調函數static void *fun(void *arg){mythread *mythis = static_cast<mythread *>(arg);// 處理任務mythis->_handler();return nullptr;}mythread(const std::string &name, handler ha) : _name(name), _handler(ha), _isdetach(false) {}~mythread() {}// 創建線程初始化tidbool start(){if (pthread_create(&_tid, nullptr, fun, this) != 0)return false;return true;}// 等待線程結束bool join(){if (_isdetach == true)return false;return pthread_join(_tid, nullptr) == 0;}// 分離線程bool setthread_detach(){_isdetach = true;return pthread_detach(_tid) == 0;}// 取消線程bool setthread_cancel(){return pthread_cancel(_tid) == 0;}pthread_t gettid() { return _tid; }private:std::string _name; // 線程名pthread_t _tid;    // 線程tidhandler _handler;  // 執行任務bool _isdetach;    // 分離線程
};

二 用戶級線程

前章說過,Linux中的線程是通過pthread庫對輕量級進程的封裝,使用前必須攜帶 -lpthread 鏈接這個庫。那么用戶級線程是如何封裝的?用戶級線程包含哪些屬性?下面來看看

既然是庫,和標準庫,第三方庫一樣,也要映射到共享區,pthread庫也不例外。

當調用pthread_create(),pthread庫會在內部維護一個用戶級線程結構,并和其他相同的結構組織起來。

當獲取線程的TID的時候,也就是pthread_create第一個參數,就是用戶級線程維護的線程的起始地址,不是內核輕量級進程的LWP字段,所以在線程操作的時候,實際是在對pthread庫操作,pthread庫封裝的輕量級進程,對庫做操作,庫幫你對輕量級進程做操作。

pthread維護的結構,包含很多字段:PID/LWP,回調函數/函數參數,void*退出信息,獨立的棧,TLS線程局部存儲.....等。

線程局部存儲:線程也可以給自己創建獨立的對象,維護在pthread結構中,但僅只支持內置類型,不支持自定義類型: __thread 類型。

獨立的棧:進程里的棧由主線程使用,即沒有創建線程的那個線程使用,其他的線程也是在pthread結構里獨立開辟空間并維護自己的棧,也就不會起沖突了。

三 同步與互斥

如果在多執行流對同一個變量進行操作會有什么問題?

假設對一個變量執行 -- 操作,當減到0結束,那么if()判斷和變量--都是操作,假設變量當前值為1,此時有多個執行流同時執行if(),if里的條件在內存中,先從內存拿到CPU,在由CPU執行,已經是2步操作了,當某個執行流對變量進行--操作,此時因某種原因被切換,比如:時間片到了,后面有優先級更高的....等,其他判斷if()條件的執行流判斷完成,此時if()內的語句已經有多條執行流了,但變量當前值為1,切走的線程又回來了進行--,后面的線程已經進來了,也會--,所以會有數據不一致問題,由并發訪問導致數據不一致問題,稱為線程安全問題。

上述根本原因是if()是由多種操作和切換等方面導致多執行流執行中有中間狀態,稱為非原子操作。

1. 概念:

原子性:執行流執行一段代碼沒有中間過程稱為原子的,這段代碼可能形成一行匯編語句,也可能是多行,只要沒有中間狀態,也就是原子的。

共享資源/臨界資源:多執行流共享一個對象,對象身為共享資源,對共享資源保護的資源,稱為臨界資源。

臨界區:臨界資源的代碼,稱為臨界區,其他稱為非臨界區。

2. 互斥量/互斥鎖:

為了保證在多線程情況下,因為并發允許導致對共享資源修改造成的數據不一致問題,提供了很多互斥機制。

互斥:訪問臨界區的代碼的時候,只有一個執行流能訪問,其他等待,所以相對于其他線程,進入臨界區的線程是原子的,由原來的并發,在臨界區中變成了串行訪問。

API:

#include <pthread.h>int pthread_mutex_init(pthread_mutex_t *mutex,               // 鎖 const pthread_mutexattr_t *mutexattr  // 鎖的屬性);int pthread_mutex_trylock(pthread_mutex_t *mutex // 申請鎖失敗返回);int pthread_mutex_destroy(pthread_mutex_t *mutex // 釋放鎖);int pthread_mutex_lock(pthread_mutex_t *mutex // 對鎖進行加鎖);int pthread_mutex_unlock(pthread_mutex_t *mutex // 對鎖進行解鎖);

如果鎖是局部變量,則需要進行初始化和手動銷毀。

全局對象則直接初始化,不用銷毀:pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER。

互斥鎖特征:如果申請成功就往下執行,否則阻塞,等待后續鎖被解鎖被喚醒重新申請鎖。

所以互斥鎖加鎖期間就是原子的。

互斥鎖實現:

當對鎖進行加鎖的時候,CPU存在一個匯編指令:swap,exchange,作用是交換內存中的值和寄存器中的值,整個下來只有一行匯編語句,也就是原子的,而之前的++/--操作而是3條匯編指令。

首先,CPU會初始化寄存器里的值為0,對應上圖第一行代碼,執行完這時如果被切走。

第一:內存中的值沒變,其他線程可以繼續申請,如果申請到,內存中的值變了,切走的線程再回來,恢復自己的寄存器的里的值:0,交換內存中的值,此時假設內存中原來的值為1,因切走被其他線程交換,變為0,此時0和0交換,在進行if()判斷,走else 阻塞等待。

第二:執行第二行代碼被切走,此時內存中的值由初始1被交換到寄存器,變為0,線程切換保存寄存器的值,后續線程的寄存器初始化為0,和內存中被交換后的值:0,0和0交換,if()不成立,else阻塞。

第三:解鎖重新交換內存中的值和寄存器的值,如果被切走,其他線程已經阻塞,新的線程可以申請,沒被切走喚醒阻塞的線程繼續申請鎖。

示例:

#include "thread.hpp"
#include <vector>// pthread_mutex_t mymtu = PTHREAD_MUTEX_INITIALIZER;
int val = 10000;
void xx(std::string s)
{while (1){// pthread_mutex_lock(&mymtu);if (val > 0){std::cout << s << " :" << val-- << std::endl;// pthread_mutex_unlock(&mymtu);}else{// pthread_mutex_unlock(&mymtu);break;}}
}
int main()
{std::vector<mythread> v;for (int i = 0; i < 5; i++)v.emplace_back(std::to_string(i), xx);for (auto &e : v)e.start();for (auto &e : v)e.join();return 0;
}

因顯示器本就是共享資源所以打印信息混亂正常,可以看到3號線程打印的val是負數,不加保護必定存在線程安全問題,所以要加鎖進行保護。

3 同步/條件變量:

上面說的互斥只是讓臨界區只有一個線程可以訪問。

同步:多個線程訪問臨界區有一定的順序。

明顯互斥也有順序,但解鎖的線程和阻塞的線程狀態不一樣,解鎖的線程解鎖完可以立即去申請鎖,而阻塞的線程要先喚醒再去申請鎖,這樣就導致了一個線程解鎖之后又能申請到鎖,而后面的鎖一直申請不到,導致的問題就是后面的線程干等,線程饑餓問題,也就是同步,但資源競爭不合理。

所以為了讓資源競爭合理,又引入了一個鎖,條件變量。

API:

// 全局不需要釋放
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;// 初始化條件變量
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);// 喚醒一個線程
int pthread_cond_signal(pthread_cond_t *cond);// 喚醒全部線程
int pthread_cond_broadcast(pthread_cond_t *cond);// 線程掛到條件變量中
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);// 釋放條件變量
int pthread_cond_destroy(pthread_cond_t *cond);

使用和互斥鎖差不多,條件變量作用就是讓線程競爭資源具有有合理性,如果資源不就緒就掛到條件變量里(隊列里),喚醒依次從隊列頭部取一個,也就保證了每個線程能合理競爭到資源。

當調用pthread_cond_wait()的時候,會釋放互斥鎖,當喚醒的時候,會彈出一個線程,并重新申請鎖。

示例:

#include <iostream>
#include <pthread.h>
#include <unistd.h>pthread_mutex_t mymtu=PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t mycond=PTHREAD_COND_INITIALIZER;int val=0;
void* fun(void* arg)
{while(1){pthread_mutex_lock(&mymtu);pthread_cond_wait(&mycond,&mymtu);std::cout<<static_cast<const char*>(arg)<<std::endl;pthread_mutex_unlock(&mymtu);}return nullptr;
}
int main()
{pthread_t t1,t2,t3;pthread_create(&t1,nullptr,fun,(void*)"thread-1");pthread_create(&t2,nullptr,fun,(void*)"thread-2");pthread_create(&t3,nullptr,fun,(void*)"thread-3");while(1){std::cout<<"wake up"<<std::endl;pthread_cond_signal(&mycond);// pthread_cond_broadcast(&mycond);sleep(1);}pthread_join(t1,nullptr);pthread_join(t2,nullptr);pthread_join(t3,nullptr);return 0;
}

當加入條件變量控制線程資源的競爭,明顯具有一定的順序性,也就是同步。

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

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

相關文章

PL/SQLDeveloper中數值類型字段查詢后顯示為科學計數法的處理方式

PL/SQLDeveloper中數值類型字段查詢后顯示為科學計數法的處理方式 文章目錄 PL/SQLDeveloper中數值類型字段查詢后顯示為科學計數法的處理方式1. 查詢效果2. 處理方式3. 再次查詢 1. 查詢效果 2. 處理方式 3. 再次查詢

centos 9/ubuntu 一次性的定時關機

方法一 # 15 表示15分鐘以后自動關機 sudo shutdown -h 15方法二&#xff1a; sudo dnf install at -y # 晚上十點半關機 echo "shutdown -h now" | at 22:30 # 檢查是否設置成功命令 atq [rootdemo-192 ~]# atq 1 Wed Jun 4 11:12:00 2025 a root # 取消定時計劃…

Riverpod與GetX的優缺點對比

Riverpod 與 GetX 的優缺點對比 在 Flutter 開發領域,Riverpod 和 GetX 都是備受關注的狀態管理與依賴注入框架,它們各有優劣,適用于不同的開發場景。以下從多個維度詳細對比二者的優缺點。 一、Riverpod 的優缺點 (一)優點 架構清晰,數據流向明確:基于 Provider 模…

day 47

注意力可視化 訓練模型 包含通道注意力模塊和CNN模型的定義&#xff08;通道注意力的插入&#xff09; import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import ma…

《Vuejs設計與實現》第 8 章(掛載與更新)

目錄 8.1 掛載子節點與屬性 8.2 HTML Attributes 與 DOM Properties 8.3 設置元素屬性的正確方式 8.4 處理 class 屬性 8.5 卸載操作 8.6 區分 vnode 類型 8.7 事件處理優化 8.8 事件冒泡與更新時機問題 8.9 子節點的更新 8.10 文本節點和注釋節點 8.11 片段&#xf…

自制操作系統(五、重寫引導部分和C語言的使用)

為了實現其他更多功能&#xff0c;我決定重新寫引導部分的內容 boot.asm ; boot.asm %include "config.inc"setuplen equ 4 bootseg equ 0x07c0 initseg equ def_initseg setupseg equ def_setupseg sysseg equ def_syssegsetupsector equ 2 syssector equ setupse…

口罩佩戴檢測算法AI智能分析網關V4工廠/工業等多場景守護公共衛生安全

一、引言? 在公共衛生安全日益受到重視的當下&#xff0c;口罩佩戴成為預防病毒傳播、保障人員健康的重要措施。為了高效、精準地實現對人員口罩佩戴情況的監測&#xff0c;AI智能分析網關V4口罩檢測方案應運而生。該方案依托先進的人工智能技術與強大的硬件性能&#xff0c;…

【評測】用Flux的圖片文本修改的PS效果

【評測】Flux的圖片文本修改的PS效果 1. 百度圖庫找一張有英文的圖片 2. 打開https://playground.bfl.ai/image/edit上傳圖片 3. 輸入提示詞 “change brarfant to goodbeer” 圖片的文字被修改了

【匯編逆向系列】三、函數調用包含單個參數之float類型-xmm0寄存器,sub,rep,stos,movss,mulss,addss指令

一、匯編代碼 single_float_param:0000000000000060: F3 0F 11 44 24 08 movss dword ptr [rsp8],xmm00000000000000066: 57 push rdi0000000000000067: 48 83 EC 10 sub rsp,10h000000000000006B: 48 8B FC mov …

深入了解UDP套接字:構建高效網絡通信

個人主頁&#xff1a;chian-ocean 文章專欄-NET 深入了解UDP套接字&#xff1a;構建高效網絡通信 個人主頁&#xff1a;chian-ocean文章專欄-NET 前言&#xff1a;UDPUDP 特點&#xff1a;UDP的應用 套接字地址IP地址&#xff08;Internet Protocol Address&#xff09;IP地址…

C++課設:實現簡易文件加密工具(凱撒密碼、異或加密、Base64編碼)

名人說&#xff1a;路漫漫其修遠兮&#xff0c;吾將上下而求索。—— 屈原《離騷》 創作者&#xff1a;Code_流蘇(CSDN)&#xff08;一個喜歡古詩詞和編程的Coder&#x1f60a;&#xff09; 專欄介紹&#xff1a;《編程項目實戰》 目錄 一、初識文件加密&#xff1a;為什么需要…

Qt/C++學習系列之Excel使用記錄

Qt/C學習系列之Excel使用記錄 前言The process was ended forcefully.解決方式斷點查語句問題 總結 前言 在項目中解析條目達50多條&#xff0c;并且都需要將對應的結果進行顯示。為了將結果顯示的更加清晰&#xff0c;考慮采用QTableWidget進行表格設置&#xff0c;而在使用過…

Mac軟件卸載指南,簡單易懂!

剛和Adobe分手&#xff0c;它卻總在Library里給你寫"回憶錄"&#xff1f;卸載的Final Cut Pro像電子幽靈般陰魂不散&#xff1f;總是會有殘留文件&#xff0c;別慌&#xff01;這份Mac軟件卸載指南&#xff0c;將用最硬核的方式教你"數字分手術"&#xff0…

并發編程實戰(生產者消費者模型)

在并發編程中使用生產者和消費者模式能夠解決絕大多數的并發問題。該模式通過平衡生產線程和消費線程的工作能力來提高程序整體處理數據的速度。 生產者和消費者模式&#xff1a; 在線程的世界中生產者就是產生數據的線程&#xff0c;而消費者則是消費數據的線程。在多線程開…

力扣hot100---152.乘積最大子數組

給你一個整數數組 nums &#xff0c;請你找出數組中乘積最大的非空連續子數組&#xff08;該子數組中至少包含一個數字&#xff09;&#xff0c;并返回該子數組所對應的乘積。 測試用例的答案是一個 32-位 整數。 示例 1: 輸入: nums [2,3,-2,4] 輸出:6解釋: 子數組 [2,3] 有最…

什么是DevOps智能平臺的核心功能?

在數字化轉型的浪潮中&#xff0c;DevOps智能平臺已成為企業提升研發效能、加速產品迭代的核心工具。然而&#xff0c;許多人對“DevOps智能平臺”的理解仍停留在“自動化工具鏈”的表層概念。今天&#xff0c;我們從一個真實場景切入&#xff1a;假設你是某互聯網公司的技術負…

柯尼卡美能達Konica Minolta bizhub 205i打印機信息

基本參數 產品類型&#xff1a;激光數碼復合機顏色類型&#xff1a;黑白涵蓋功能&#xff1a;復印、打印、掃描最大原稿尺寸&#xff1a;A3內存容量&#xff1a;256MB供紙容量&#xff1a;標配 350 頁&#xff0c;最大 1350 頁介質重量&#xff1a;標準紙盒 64-157g/㎡&#xf…

虛擬機與宿主機應用通信配置指南

1. 選擇虛擬機網絡模式 橋接模式 (Bridged) 客戶機獲得獨立局域網IP&#xff0c;與宿主機同網段。 客戶機可直接訪問宿主機IP&#xff08;如 192.168.1.x&#xff09;。 Host-Only 模式 僅宿主機與客戶機之間通信&#xff0c;宿主機通常有一個虛擬網卡&#xff08;如 192.16…

網絡庫libhv介紹

libhv是一個類似于libevent、libev、libuv的跨平臺網絡庫&#xff0c;提供了更易用的接口和更豐富的協議&#xff0c;用來開發TCP/UDP/SSL/HTTP/WebSocket/MQTT 客戶端/服務端。源碼地址&#xff1a;https://github.com/ithewei/libhv&#xff0c;最新發布版本為v1.3.3&#xf…

施耐德特價型號伺服電機VIA0703D31A1022、常見故障

?? ?一、啟動類故障? ?電機無法啟動? ?可能原因?&#xff1a;電源未接通、制動器未釋放、接線錯誤或控制器故障。?解決措施?&#xff1a; 檢查電源線路及斷路器狀態&#xff1b;驗證制動器是否打開&#xff08;帶制動器型號&#xff09;&#xff1b;核對電機與控制器…