系統編程.9 線程

1.線程概述

程序運行起來編程進程,進程由一個個線程構成。

eg:

沒有啟動的qq時一個程序,啟動后登錄qq,qq是一個進程,實際上進程什么都沒做,只是提供了需要的資源,打開聊天框可以和別人進行通信,這就是線程,和多個人聊天就是多線程。

每個進程中至少包含一個主線程。

線程是輕量級的進程(LWP:light weight process),再linux環境下的本質仍然是基礎。

進程是最小的資源分配單位,線程是最小的執行單位。

2.線程資源

線程只共享:

  • 文件描述符
  • 每種信號的處理方式
  • 當前工作目錄
  • 用戶ID和組ID,內存地址空間

獨占的資源:(棧區不共享)

  • 線程ID
  • 處理器現場和棧指針(內核棧)
  • 獨立的棧空間(用戶空間棧)
  • errno變量
  • 信號屏蔽字
  • 調度優先級

3.線程的優點和缺點

優點:

  • 提高程序并發性
  • 開銷小
  • 數據通信、共享數據方便

缺點:

  • 庫函數,不穩定
  • 調試、編寫困難、gdb不支持
  • 對信號支持不好

4.線程的創建

4.1 線程號

線程號在對應的進程里是唯一的。

進程號:pid_t 非負整數

線程號:pthread_t 無符號長整型

4.2 API

4.2.1?pthread_self()

#include <pthread.h>

pthread_t pthread_self(void);

函數功能:

  • 獲取當前線程的線程號

返回值:

  • 獲取的線程號

后續如果遇到編譯不通過,提示pthread相關,可以編譯的時候引入相關資源。 -pthread

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>int main(int argc, char const *argv[])
{pid_t pid=getpid();//獲取進程號pthread_t pt1=pthread_self();//獲取當前進程的主線程號printf("pid:%u,%lu\n",pid,pt1);  return 0;
}

4.2.2?pthread_create()

#include <pthread.h>

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

函數功能:

  • 創建一個線程

參數:

  • thread:線程標識符地址。
  • attr:線程屬性結構體地址,通常設置為 NULL。
  • start_routine:線程函數的入口地址。函數指針。
  • arg:傳給線程函數的參數。

返回值:

  • 成功:0
  • 失敗:非 0

線程函數不傳參:

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>void *func(void *argv)
{printf("線程%lu正在運行\n", pthread_self());
}int main(int argc, char const *argv[])
{pid_t pid = getpid();           // 獲取進程號pthread_t pt1 = pthread_self(); // 獲取當前進程的主線程號printf("進程:%u,主線程:%lu\n", pid, pt1);pthread_t pt2;int ret = pthread_create(&pt2, NULL, func, NULL); // 創建線程if (ret != 0){perror("pthread_create");return 0;}// 在此處阻塞,避免線程未執行,進程就結束,進程結束,系統會回收進程資源// 線程之間是并發的,而不是簡單的從上往下執行sleep(1);return 0;
}

線程函數傳參:數組

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>void *func(void *argv)
{printf("線程:%lu 內容:%s\n", pthread_self(), (char *)argv);// 此處char*不能省,因為cpu訪問內存讀取數據,必須要約束類型
}int main(int argc, char const *argv[])
{pid_t pid = getpid();           // 獲取進程號pthread_t pt1 = pthread_self(); // 獲取當前進程的主線程號printf("進程:%u,主線程:%lu\n", pid, pt1);char buff[10] = "hello";pthread_t pt2;int ret = pthread_create(&pt2, NULL, func, buff); // 創建線程// int ret =pthread_create(&pt1,NULL,func,(void *)buff);// 以上兩種都可以,因為地址本身沒有類型,char *  void*都可以,只是地址而已if (ret != 0){perror("pthread_create");return 0;}// 在此處阻塞,避免線程未執行,進程就結束,進程結束,系統會回收進程資源// 線程之間是并發的,而不是簡單的從上往下執行sleep(1);return 0;
}

線程函數傳參:整數

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>void *func(void *argv)
{*(int *)argv = 20;
}int main(int argc, char const *argv[])
{pthread_t pt1 = pthread_self();int num = 10;int ret = pthread_create(&pt1, NULL, func, &num);// int ret =pthread_create(&pt1,NULL,func,(void *)num);// 以上兩種都可以,因為地址本身沒有類型,int *  void*都可以,只是地址而已if (ret != 0){perror("pthread_create");return 0;}sleep(1);printf("num=%d\n", num);return 0;
}

4.2.3 多線程創建

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
pthread_t pt1, pt2;
void *func1(void *argv)
{int count = 1;while (1){printf("線程:%lu:%s,執行了%ds\n", pt1, (char *)argv, count);count++;if (count == 5){break;}}
}
void *func2(void *argv)
{int count = 0;while (1){printf("線程:%lu:%s,執行了%ds\n", pt2, (char *)argv, count);count++;if (count == 5){break;}}
}int main(int argc, char const *argv[])
{pt1 = pthread_self();pt2 = pthread_self();int ret1 = pthread_create(&pt1, NULL, func1, NULL);if (ret1 != 0){perror("pthread_create");return 0;}int ret2 = pthread_create(&pt2, NULL, func2, NULL);if (ret2 != 0){perror("pthread_create");return 0;}sleep(1);return 0;
}

4.3.4 pthread_join()

#include <pthread.h>

int pthread_join(pthread_t thread, void **retval);

函數功能:

  • 等待線程結束(此函數會阻塞),并回收線程資源,類似進程的 wait() 函數。如果線程已經結束,那么該函數會立即返回。

參數:

  • thread:被等待的線程號。
  • retval:用來存儲線程退出狀態的指針的地址,二級指針

返回值:

  • 成功:0
  • 失敗:非 0
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
pthread_t pt1, pt2;
void *func1(void *argv)
{int count = 1;while (1){printf("線程:%lu:%s,執行了%ds\n", pt1, (char *)argv, count);count++;if (count == 5){break;}}return "func1";
}
void *func2(void *argv)
{int count = 0;while (1){printf("線程:%lu:%s,執行了%ds\n", pt2, (char *)argv, count);count++;if (count == 5){break;}}
}int main(int argc, char const *argv[])
{pt1 = pthread_self();pt2 = pthread_self();int ret1 = pthread_create(&pt1, NULL, func1, NULL);if (ret1 != 0){perror("pthread_create");return 0;}int ret2 = pthread_create(&pt2, NULL, func2, NULL);if (ret2 != 0){perror("pthread_create");return 0;}void *returnvalue;printf("returnvalue:%p\n",returnvalue);pthread_join(pt1,&returnvalue);//pt1 返回地址是  "func1" 這個字符串的首地址printf("pt1退出狀態:%s\n",(char *)returnvalue);return 0;
}

棧區數據不會共享,返回棧區數據導致段錯誤

函數返回棧區數據段錯誤示例
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
pthread_t pt1, pt2;void *func2(void *argv)
{int num=10;int count = 0;while (1){printf("線程:%lu:%s,執行了%ds\n", pt2, (char *)argv, count);count++;if (count == 5){break;}}return (void *)&num;
}int main(int argc, char const *argv[])
{pt2 = pthread_self();int ret2 = pthread_create(&pt2, NULL, func2, NULL);if (ret2 != 0){perror("pthread_create");return 0;}void *returnvalue;printf("returnvalue:%p\n",returnvalue);pthread_join(pt2,&returnvalue);//pt2 返回地址是  "func2" 這個字符串的首地址printf("pt2退出狀態:%s\n",(char *)returnvalue);return 0;
}

返回堆區地址:

#include <pthread.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>void *func1(void *argv);
void *func2(void *argv);int main(int argc, char const *argv[])
{pthread_t pt1, pt2;int ret1 = pthread_create(&pt1, NULL, func1, NULL);if (ret1 != 0){perror("pthread_creat()");}int ret2 = pthread_create(&pt2, NULL, func2, NULL);if (ret2 != 0){perror("pthread_creat()");}void *returnvalue;void *returnvalue2;pthread_join(pt1, &returnvalue); // pt1 返回地址是  "pthread_func1" 這個字符串的首地址pthread_join(pt2, &returnvalue2); // printf("pt1退出狀態取值:%s\n", (char *)returnvalue); //強轉,取字符串 結果 "pthread_func1"printf("pt2退出的地址取值:%d\n",*(int *)returnvalue2);return 0;
}void *func1(void *argv)
{int count = 1;while (1){printf("當前所在進程%u,當前線程%lu,執行了%dS\n", getpid(), pthread_self(), count);sleep(1);count++;if (count >= 6){break;}}return "pthread_func1";
}void *func2(void *argv)
{int *p = (int *)malloc(sizeof(int));*p = 10;int count = 1;while (1){printf("當前所在進程%u,當前線程%lu,執行了%dS\n", getpid(), pthread_self(), count);sleep(1);count++;if (count >= 6){break;}} return p; //不能是棧區地址,因為棧區地址不共享,每一個線程都有自己的棧
}

4.3.5pthread_detach()

一般情況下,線程終止后,其終止狀態一直保留到其它線程調用 pthread_join 獲取它的狀態為止。但是線程也可以被置為 detach 狀態,這樣的線程一旦終止就立刻回收(系統回收)它占用的所有資源,而不保留終止狀態。

所以如果回收線程資源: pthread_join與pthread_detach只能二選一

?#include <pthread.h>

int pthread_detach(pthread_t thread);

函數功能:

  • 將指定線程標記為分離狀態。

參數:

  • pthread :指定的要分離的線程

4.3.6?pthread_exit()

#include <pthread.h>

void pthread_exit(void *retval);

函數功能:

  • 退出調用線程。一個進程中的多個線程是共享該進程的數據段,因此,通常線程退出后所占用的資源并不會釋放。

參數:

  • retval:存儲線程退出狀態的指針。
#include <pthread.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#define sucess 0
#define error 1void *pthread_func1(void *argv);int main(int argc, char const *argv[])
{pthread_t pt1;char buff[] = "hello my thread";int ret = pthread_create(&pt1, NULL, pthread_func1, buff); // 創建線程,線程函數傳參printf("pt1=%lu\n", pt1);if (ret != 0){perror("pthread_create())");return 0;}printf("在進程%u中,主線程號:%lu\n", getpid(), pthread_self());void *return_value;pthread_join(pt1, &return_value);if (return_value == NULL) // 0{printf("success\n");}else{printf("error\n"); // 1}// free(return_value);return 0;
}void *pthread_func1(void *argv)
{for (int i = 1; i <= 5; i++){printf("線程%lu,執行了%dS\n", pthread_self(), i);sleep(1);if (i == 5){pthread_exit((void *)sucess); // void * + 數值,直接轉換為地址}}return NULL;
}

4.3.7?pthread_cancel()

#include <pthread.h>

int pthread_cancel(pthread_t thread);

函數功能:

  • 殺死(取消)線程

參數:

  • thread : 目標線程 ID。

返回值:

  • 成功:0
  • 失敗:出錯編號

線程的取消并不是實時的,而有一定的延時。

需要等待線程到達某個取消點。?

取消點:執行命令 man 7 pthreads 可以查看具備這些取消點的系統調用列表。 可粗略認為一個系統調用(進入內核)即為一個取消點,例如sleep();

#include <pthread.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>void *myfunc1(void *argv);int main(int argc, char const *argv[])
{pthread_t pt1;int ret = pthread_create(&pt1,NULL,myfunc1,NULL);pthread_cancel(pt1);while (1){/* code */}return 0;
}void *myfunc1(void *argv)
{while (1){printf("當前所在進程%u,當前線程%lu\n",getpid(),pthread_self());sleep(1);printf("check\n"); //cancel完不會執行,因為上面有取消點}}

5.線程的屬性

typedef struct
{
int etachstate; //線程的分離狀態
int  schedpolicy; //線程調度策略
struct sched_param schedparam; //線程的調度參數
int  inheritsched; //線程的繼承性
int  scope; //線程的作用域
size_t guardsize; //線程棧末尾的警戒緩沖區大小
int  stackaddr_set; //線程的棧設置
void* stackaddr; //線程棧的位置
size_t stacksize; //線程棧的大小
} pthread_attr_t;

提前設置分離屬性

  • 先進行屬性初始化
  • 設置某一個屬性
  • 創建線程
  • 銷毀線程屬性

5.1?pthread_attr_init()

int pthread_attr_init(pthread_attr_t *attr);

函數功能:

  • 對線程屬性結構體 attr初始化

參數:

  • attr 要初始化的結構體

5.2?pthread_attr_setdetachstate()

int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);

函數功能:

  • 線程分離屬性設置

參數:

  • attr:已初始化的線程屬性
  • detachstate:分離狀態

PTHREAD_CREATE_DETACHED(分離線程)

PTHREAD_CREATE_JOINABLE(非分離線程)

5.3?

int pthread_attr_destroy(pthreadattrt *attr);

函數功能:

  • 線程銷毀屬性設置

參數:

  • attr:要銷毀的線程屬性

函數返回值:

  • 成功:0;
  • 失敗:錯誤號
#define _POSIX_SOURCE
#include <pthread.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>void *myfunc1(void *argv);int main(int argc, char const *argv[])
{pthread_t pt1;pthread_attr_t attr;// 初始化pthread_attr_init(&attr);// 設置分離屬性pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);// 創建線程int ret = pthread_create(&pt1, &attr, myfunc1, NULL);// destorypthread_attr_destroy(&attr);while (1){/* code */}return 0;
}void *myfunc1(void *argv)
{while (1){printf("當前所在進程%u,當前線程%lu\n", getpid(), pthread_self());sleep(1);}
}

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

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

相關文章

2.10組件間的通信

1.Vue組件的嵌套關系1.1認識組件的嵌套前面我們是將所有的邏輯放到一個 App.vue 中&#xff1b;在之前的案例中&#xff0c;我們只是創建了一個組件 App&#xff1b;如果我們一個應用程序所有的邏輯都放在一個組件中&#xff0c;那么這個組件就會變成非常的臃腫和難以維護&…

Mybatis-Plus學習筆記

目錄 一、MyBatis-Plus簡介 二、MyBatisPlus使用的基本流程&#xff1a; &#xff08;1&#xff09;引入MybatisPlus依賴&#xff0c;代替MyBatis依賴 &#xff08;2&#xff09;自定義Mapper繼承BaseMapper ?編輯&#xff08;3&#xff09;在實體類上添加注解聲明表信息…

Day22 用C語言編譯應用程序

文章目錄1. 保護操作系統5&#xff08;harib19a&#xff09;2. 幫助發現bug&#xff08;harib19b&#xff09;3. 強制結束應用程序&#xff08;harib19c&#xff09;4. 用C語言顯示字符串&#xff08;harib19e&#xff09;5. 顯示窗口&#xff08;harib19f&#xff09;1. 保護操…

簡單學習HTML+CSS+JavaScript

一、HTML HTML被稱為 超文本標記語言&#xff0c;是由一系列標簽構成的語言。 下面介紹HTML中的標簽&#xff1a; &#xff08;一&#xff09;HTML文件基本結構 <!DOCTYPE html><html><head><title>Document</title></head> <body&…

強化學習中重要性采樣

PPO 中重要性采樣 https://github.com/modelscope/ms-swift/blob/main/docs/source/Instruction/GRPO/GetStarted/GRPO.md樂&#xff0c;這個網頁中是的groundtruth是錯誤的&#xff08;可能是為了防止抄襲&#xff09;。一些例子 0. 池塘養魚的一個例子 想象一下&#xff0c;你…

《樹與二叉樹詳解:概念、結構及應用》

目錄 一. 樹的概念和結構 1.1 樹的基本概念 1.2 樹的結構特點 二. 樹的表示方法和實際運用 2.1 孩子 - 兄弟表示法&#xff08;Child-Sibling Representation&#xff09; 2.2 樹的實際應用場景 三. 二叉樹的概念 3.1 二叉樹的核心定義 3.2 二叉樹的基本分類 四. 二叉…

Qt/C++,windows多進程demo

1. 項目概述 最近研究了一下Qt/C框架下&#xff0c;windows版本的多進程編寫方法&#xff0c;實現了一個小demo。下面詳細介紹一下。 MultiProcessDemo是一個基于Qt框架實現的多進程應用程序示例&#xff0c;展示了如何在Windows平臺上通過共享內存和事件機制實現進程間通信。該…

Android SystemServer 系列專題【篇五:UserController用戶狀態控制】

本篇接著SystemServer的啟動流程&#xff0c;圍繞SystemServer最后階段關于主用戶的啟動和解鎖的流程&#xff0c;作為切入點&#xff0c;來看看SystemServer是如何講用戶狀態同步到所有的系統級服務中。ssm.onStartUserssm.onUnlockingUserssm.onUnlockedUser本篇先介紹UserCo…

推薦使用 pnpm 而不是 npm

npm 的局限性 磁盤空間浪費在 npm 早期版本中&#xff0c;每個項目的node_modules目錄都會完整復制所有依賴包&#xff0c;即使多個項目依賴同一個包的相同版本&#xff0c;也會重復存儲。這導致磁盤空間被大量占用&#xff0c;隨著項目數量的增加&#xff0c;存儲成本顯著上升…

Transformer實戰(18)——微調Transformer語言模型進行回歸分析

Transformer實戰&#xff08;18&#xff09;——微調Transformer語言模型進行回歸分析0. 前言1. 回歸模型2. 數據處理3. 模型構建與訓練4. 模型推理小結系列鏈接0. 前言 在自然語言處理領域中&#xff0c;預訓練 Transformer 模型不僅能勝任離散類別預測&#xff0c;也可用于連…

【Linux】【實戰向】Linux 進程替換避坑指南:從理解 bash 阻塞等待,到親手實現能執行 ls/cd 的 Shell

前言&#xff1a;歡迎各位光臨本博客&#xff0c;這里小編帶你直接手撕&#xff0c;文章并不復雜&#xff0c;愿諸君耐其心性&#xff0c;忘卻雜塵&#xff0c;道有所長&#xff01;&#xff01;&#xff01;&#xff01; IF’Maxue&#xff1a;個人主頁&#x1f525; 個人專欄…

linux常用命令 (3)——系統包管理

博客主頁&#xff1a;christine-rr-CSDN博客 ????? ?? hi&#xff0c;大家好&#xff0c;我是christine-rr ! 今天來分享一下linux常用命令——系統包管理 目錄linux常用命令---系統包管理&#xff08;一&#xff09;Debian 系發行版&#xff08;Ubuntu、Debian、Linux …

YOLOv8 mac-intel芯片 部署指南

&#x1f680; 在 Jupyter Notebook 和 PyCharm 中使用 Conda 虛擬環境&#xff08;YOLOv8 部署指南&#xff0c;Python 3.9&#xff09; YOLOv8 是 Ultralytics 開源的最新目標檢測模型&#xff0c;輕量高效&#xff0c;支持分類、檢測、分割等多種任務。 在 Mac&#xff08;…

【高等數學】第十一章 曲線積分與曲面積分——第六節 高斯公式 通量與散度

上一節&#xff1a;【高等數學】第十一章 曲線積分與曲面積分——第五節 對坐標的曲面積分 總目錄&#xff1a;【高等數學】 目錄 文章目錄1. 高斯公式2. 沿任意閉曲面的曲面積分為零的條件3. 通量與散度1. 高斯公式 設空間區域ΩΩΩ是由分片光滑的閉曲面ΣΣΣ所圍成&#x…

IDEA試用過期,無法登錄,重置方法

IDEA過期&#xff0c;重置方法: IntelliJ IDEA 2024.2.0.2 (親測有效) 最新Idea重置辦法!&#xff1a; 方法一&#xff1a; 1、刪除C:\Users\{用戶名}\AppData\Local\JetBrains\IntelliJIdea2024.2 下所有文件(注意&#xff1a;是子目錄全部刪除) 2、刪除C:\Users\{用戶名}\App…

創建用戶自定義橋接網絡并連接容器

1.創建用戶自定義的 alpine-net 網絡[roothost1 ~]# docker network create --driver bridge alpine-net 9f6d634e6bd7327163a9d83023e435da6d61bc6cf04c9d96001d1b64eefe4a712.列出 Docker 主機上的網絡[roothost1 ~]# docker network ls NETWORK ID NAME DRIVER …

Vue3 + Vite + Element Plus web轉為 Electron 應用,解決無法登錄、隱藏自定義導航欄

如何在vue3 Vite Element Plus搭好的架構下轉為 electron應用呢&#xff1f; https://www.electronjs.org/zh/docs/latest/官方文檔 https://www.electronjs.org/zh/docs/latest/ 第一步&#xff1a;安裝 electron相關依賴 npm install electron electron-builder concurr…

qt QAreaLegendMarker詳解

1. 概述QAreaLegendMarker 是 Qt Charts 模塊中的一部分&#xff0c;用于在圖例&#xff08;Legend&#xff09;中表示 QAreaSeries 的標記。它負責顯示區域圖的圖例項&#xff0c;通常包含區域顏色樣例和對應的描述文字。圖例標記和對應的區域圖關聯&#xff0c;顯示區域的名稱…

linux 函數 kstrtoul

kstrtoul 函數概述 kstrtoul 是 Linux 內核中的一個函數&#xff0c;用于將字符串轉換為無符號長整型&#xff08;unsigned long&#xff09;。該函數定義在 <linux/kernel.h> 頭文件中&#xff0c;常用于內核模塊中解析用戶空間傳遞的字符串參數。 函數原型 int kstrtou…

LLM(三)

一、人類反饋的強化學習&#xff08;RLHF&#xff09;微調的目標是通過指令&#xff0c;包括路徑方法&#xff0c;進一步訓練你的模型&#xff0c;使他們更好地理解人類的提示&#xff0c;并生成更像人類的回應。RLHF&#xff1a;使用人類反饋微調型語言模型&#xff0c;使用強…