首頁 運維 linux運維 linux下C語言的多執行緒程式設計的詳細介紹

linux下C語言的多執行緒程式設計的詳細介紹

Oct 14, 2017 am 11:00 AM
linux 程式設計 詳細

這篇文章主要介紹了linux下c語言的多線程編程,需要的朋友可以參考下

我們在寫linux的服務的時候,經常會用到linux的多線程技術以提高程式效能 

多執行緒的一些小知識:

一個應用程式可以啟動若干個執行緒。

線程(Lightweight Process,LWP),是程式執行的最小單元。

一般一個最簡單的程式最少會有一個執行緒,就是程式本身,也就是主函數(單執行緒的行程可以簡單的認為只有一個執行緒的行程)

 一個執行緒阻塞並不會影響到另外一個線程。

多執行緒的進程可以盡可能的利用系統CPU資源。

1建立執行緒

先上一段在一個行程中建立一個執行緒的簡單的程式碼,然後慢慢深入。


#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
void * func(void * arg)
{
 printf("func run...\n");
 return NULL;
}
int main()
{
 pthread_t t1;
 int err = pthread_create(&t1,NULL,func,NULL);
 if(err!=0)
 {
  printf("thread_create Failed:%s\n",strerror(errno));
 }else{
  printf("thread_create success\n");
 }
 sleep(1);
 return EXIT_SUCCESS;
}
int pthread_create(pthread_t *thread,const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg);
登入後複製

在main函數裡面我們呼叫上面的函數進行建立一個執行緒。

函數參數:

  第一個參數:pthread_t代表建立執行緒的唯一標識,是一個結構體,需要我們建立好後,將這個結構體的指針傳遞過去。

  第二個參數:pthread_attr_t,代表創建這個執行緒的一些配置,例如分配堆疊的大小等等。 。一般我們可以填NULL,代表預設的建立執行緒的配置

  第三個參數:代表一個函數的位址,建立執行緒時,會呼叫這個函數,函數的回傳值是void*,函數的參數也是void*,一般格式就像void * func(void * arg){}

#  第四個參數:代表呼叫第三個函數傳遞的參數

#函數傳回值:

  函數成功傳回0,如果不等於0則代表函數呼叫失敗,此時透過strerror(errno)可以印出具體的錯誤。

  注意:每個執行緒都擁有一份errno副本,不同的執行緒擁有不同的errno

最後透過gcc編譯


gcc 1createthread.c -c -o 1createthread.o
gcc 1createthread.o -o thr1 -lpthread
登入後複製

編譯的時候需要加上-lpthread 用來連結libpthread.so動態函式庫,不然會提示找不到function

函式呼叫回傳結果

##問題:為什麼呼叫sleep函數

答案:可能新建立的執行緒還沒運行到列印的方法主執行緒就結束了,而主執行緒結束,所有線程都會結束了。

2線程掛起

有時候我們在一個線程中創建了另外一個線程,主線程要等到創建的線程返回了,取得該執行緒的回傳值後主執行緒才退出。這時候就需要用到線程掛起。


int pthread_join(pthread_t th, void **thr_return);。
登入後複製

pthread_join函數用來掛起目前線程,直到th指定的執行緒終止為止。


#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
void * func(void * arg)
{
 int i=0;
 for(;i<5;i++)
 {
  printf("func run%d\n",i);
  sleep(1);
 }
 int * p = (int *)malloc(sizeof(int));
 *p=11;
 return p;
}
int main()
{
 pthread_t t1,t2;
 int err = pthread_create(&t1,NULL,func,NULL);
 if(err!=0)
 {
  printf("thread_create Failed:%s\n",strerror(errno));
 }else{
  printf("thread_create success\n");
 }
 void *p=NULL;
 pthread_join(t1,&p);
 printf("线程退出:code=%d\n",*(int*)p);
 return EXIT_SUCCESS;
}
登入後複製

函數執行結果

#我們主函數一直在等待創建的線程執行完,並且得到了線程執行結束的回傳值

3執行緒終止

#進程終止時exit()函數,那麼執行緒終止是什麼呢?

執行緒終止的三種情況:

執行緒只是從啟動函數中傳回,回傳值是執行緒的退出碼。

執行緒可以被同一行程中的其他執行緒取消。

執行緒呼叫pthread_exit。


#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
void * func(void * arg)
{
 int i=0;
 while(1)
 {
  if(i==10)
  {
   int * p = (int *)malloc(sizeof(int));
   *p=11;
   pthread_exit(p);
  }
  printf("fun run %d\n",i++);
  sleep(1);
 }
 return NULL;
}
int main()
{
 pthread_t t1,t2;
 int err = pthread_create(&t1,NULL,func,NULL);
 if(err!=0)
 {
  printf("thread_create Failed:%s\n",strerror(errno));
 }else{
  printf("thread_create success\n");
 }
 void *p=NULL;
 pthread_join(t1,&p);
 printf("线程退出:code=%d",*(int*)p);
 return EXIT_SUCCESS;
}
void pthread_exit(void *arg);
登入後複製

pthread_exit函數的參數就跟正常執行緒結束return的使用時一樣的,都會被等待它結束的主執行緒取得到。

函數執行結果:

4執行緒分離


#
int pthread_detach(pthread_t th);
登入後複製

pthread_detach函數使執行緒處於被分離狀態。

如果不等待一個線程,同時對線程的回傳值不感興趣,可以設定這個執行緒為被分離狀態,讓系統在執行緒退出的時候自動回收它所佔用的資源。

一個執行緒不能自己呼叫pthread_detach改變自己為被分離狀態,只能由其他執行緒呼叫pthread_detach。 5執行緒取消


#
int pthread_cancel(pthread_t th);
登入後複製

pthread_cancel函數允許一個執行緒取消th指定的另一個執行緒。

函數成功,回傳0,否則回傳非0。

#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
void * func1(void * arg)
{
 while(1)
 {
  printf("fun run...\n");
  sleep(1);
 }
 return NULL;
}
int main()
{
 pthread_t t1;
 if(pthread_create(&t1,NULL,func1,NULL)!=0)
 {
  printf("thread_create Failed:%s\n",strerror(errno));
  return -1;
 }
 sleep(5);
 pthread_cancel(t1);
 pthread_join(t1,NULL);
 return EXIT_SUCCESS;
}
登入後複製

 函數執行結果:

######

上面我们说过创建一个线程函数pthread_create的第二个参数,用来决定创建线程的一些初始化状态,这里我们 举个例子,改线程一创建就是分离状态的线程(

上面介绍了pthread_detach函数的概念,可以通过pthread_attr_t在创建线程的时候就指定线程属性为detach,而不用创建以后再去修改线程属性。

先上一段代码:


#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
void * func(void * arg)
{
 int i=0;
 for(;i<5;i++)
 {
  printf("func run%d\n",i);
  sleep(1);
 }
 int * p = (int *)malloc(sizeof(int));
 *p=11;
 return p;
}
int main()
{
 pthread_t t1;
 pthread_attr_t attr;//申明一个attr的结构体
 pthread_attr_init(&attr);//初始化结构体
 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);//设置线程为分离线程
 int err = pthread_create(&t1,&attr,func,NULL);
 if(err!=0)
 {
  printf("thread_create Failed:%s\n",strerror(errno));
 }else{
  printf("thread_create success\n");
 }
 pthread_attr_destroy(&attr);
 pthread_join(t1,NULL);
 printf("主线程退出\n");
 return EXIT_SUCCESS;
}
登入後複製

pthread_attr_t就是我们要传入的参数的结构体,一般申明的步骤有

1,申明一个pthread_attr_t对象

2,函数pthread_attr_init初始化attr结构。

3,设置线程的一些属性,比如pthread_attr_setdetachstate函数就是设置该线程创建的时候为正常状态还是分离状态。

4,函数pthread_attr_destroy释放attr内存空间

pthread_attr_setdetachstate把线程属性设置为下面两个合法值之一:

说明

PTHREAD_CREATE_DETACHED

设置线程为分离状态

PTHREAD_CREATE_JOINABLE

设置线程为正常状态

上面函数运行结果:

因为线程是个分离状态的,所以pthread_join挂起会失效,主线程很快运行结束,程序也就结束了,创建的线程还没来得及运行

线程同步

有时候我们多个线程处理订单扣减库存会遇到这样的问题,两个线程同时进入一段代码先查询库存,两个都查出来为还剩一件库存,第一个线程用掉这个库存后,将库存变为0,但是第二个线程刚才也查出来为1了,所以他还认为有库存,

这个时候操作就会引发我们想不到的意外,库存变为负数了!!所以这个时候就需要使用线程的同步!!

先上一段代码看看效果:


#include<pthread.h>
#include<stdio.h>
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
void * func(void * arg)
{
 int threadno =*(int*)arg;
 int i=0;
 for(;i<10;i++)
 {
  printf("%d thread%d \n",threadno,i);
  sleep(1);
 }
 return NULL;
}
int main()
{
 pthread_t t1,t2;
 int i1=1,i2=2;
 pthread_create(&t1,NULL,func,&i1);
 pthread_create(&t2,NULL,func,&i2);
 pthread_join(t1,NULL);
 pthread_join(t2,NULL);
 printf("主线程退出\n");
 return EXIT_SUCCESS;
}
登入後複製

函数运行结果:

可以看到两个线程是没有规律的争相处理的,如果这段代码是扣减库存就完蛋啦!,所以我们要对这段代码进行加锁,同一时刻只能有一个线程进入操作!

先上代码:


#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void * func(void * arg)
{
 pthread_mutex_lock(&mutex);//对mutex加锁,其他线程进入后将会挂起,知道这个锁被解锁
 int threadno =*(int*)arg;
 int i=0;
 for(;i<10;i++)
 {
  printf("%d thread%d \n",threadno,i);
  sleep(1);
 }
 pthread_mutex_unlock(&mutex);
 return NULL;
}
int main()
{
 pthread_t t1,t2;
 int i1=1,i2=2;
 pthread_create(&t1,NULL,func,&i1);
 pthread_create(&t2,NULL,func,&i2);
 pthread_join(t1,NULL);
 pthread_join(t2,NULL);
 printf("主线程退出\n");
 return EXIT_SUCCESS;
}
登入後複製

函数运行结果:

可以看到第二个线程先进入后一直运行结束,对mutex解锁后,第一个线程才能进方法里面运行!否则会挂起,一直等到锁被解锁!

PTHREAD_MUTEX_INITIALIZER是初始化一个快速锁的宏定义。


pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
登入後複製

加锁解锁函数:


int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
登入後複製

总结

以上是linux下C語言的多執行緒程式設計的詳細介紹的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.聊天命令以及如何使用它們
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

centos和ubuntu的區別 centos和ubuntu的區別 Apr 14, 2025 pm 09:09 PM

CentOS 和 Ubuntu 的關鍵差異在於:起源(CentOS 源自 Red Hat,面向企業;Ubuntu 源自 Debian,面向個人)、包管理(CentOS 使用 yum,注重穩定;Ubuntu 使用 apt,更新頻率高)、支持週期(CentOS 提供 10 年支持,Ubuntu 提供 5 年 LTS 支持)、社區支持(CentOS 側重穩定,Ubuntu 提供廣泛教程和文檔)、用途(CentOS 偏向服務器,Ubuntu 適用於服務器和桌面),其他差異包括安裝精簡度(CentOS 精

centos如何安裝 centos如何安裝 Apr 14, 2025 pm 09:03 PM

CentOS 安裝步驟:下載 ISO 映像並刻錄可引導媒體;啟動並選擇安裝源;選擇語言和鍵盤佈局;配置網絡;分區硬盤;設置系統時鐘;創建 root 用戶;選擇軟件包;開始安裝;安裝完成後重啟並從硬盤啟動。

Centos停止維護2024 Centos停止維護2024 Apr 14, 2025 pm 08:39 PM

CentOS將於2024年停止維護,原因是其上游發行版RHEL 8已停止維護。該停更將影響CentOS 8系統,使其無法繼續接收更新。用戶應規劃遷移,建議選項包括CentOS Stream、AlmaLinux和Rocky Linux,以保持系統安全和穩定。

Centos停止維護後的選擇 Centos停止維護後的選擇 Apr 14, 2025 pm 08:51 PM

CentOS 已停止維護,替代選擇包括:1. Rocky Linux(兼容性最佳);2. AlmaLinux(與 CentOS 兼容);3. Ubuntu Server(需要配置);4. Red Hat Enterprise Linux(商業版,付費許可);5. Oracle Linux(與 CentOS 和 RHEL 兼容)。在遷移時,考慮因素有:兼容性、可用性、支持、成本和社區支持。

docker原理詳解 docker原理詳解 Apr 14, 2025 pm 11:57 PM

Docker利用Linux內核特性,提供高效、隔離的應用運行環境。其工作原理如下:1. 鏡像作為只讀模板,包含運行應用所需的一切;2. 聯合文件系統(UnionFS)層疊多個文件系統,只存儲差異部分,節省空間並加快速度;3. 守護進程管理鏡像和容器,客戶端用於交互;4. Namespaces和cgroups實現容器隔離和資源限制;5. 多種網絡模式支持容器互聯。理解這些核心概念,才能更好地利用Docker。

centos停止維護後怎麼辦 centos停止維護後怎麼辦 Apr 14, 2025 pm 08:48 PM

CentOS 停止維護後,用戶可以採取以下措施應對:選擇兼容髮行版:如 AlmaLinux、Rocky Linux、CentOS Stream。遷移到商業發行版:如 Red Hat Enterprise Linux、Oracle Linux。升級到 CentOS 9 Stream:滾動發行版,提供最新技術。選擇其他 Linux 發行版:如 Ubuntu、Debian。評估容器、虛擬機或云平台等其他選項。

docker desktop怎麼用 docker desktop怎麼用 Apr 15, 2025 am 11:45 AM

如何使用 Docker Desktop? Docker Desktop 是一款工具,用於在本地機器上運行 Docker 容器。其使用步驟包括:1. 安裝 Docker Desktop;2. 啟動 Docker Desktop;3. 創建 Docker 鏡像(使用 Dockerfile);4. 構建 Docker 鏡像(使用 docker build);5. 運行 Docker 容器(使用 docker run)。

vscode需要什麼電腦配置 vscode需要什麼電腦配置 Apr 15, 2025 pm 09:48 PM

VS Code 系統要求:操作系統:Windows 10 及以上、macOS 10.12 及以上、Linux 發行版處理器:最低 1.6 GHz,推薦 2.0 GHz 及以上內存:最低 512 MB,推薦 4 GB 及以上存儲空間:最低 250 MB,推薦 1 GB 及以上其他要求:穩定網絡連接,Xorg/Wayland(Linux)

See all articles