精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

一文搞懂POSIX多線程:解鎖高性能編程的密碼

開發 前端
對于多線程編程而言,POSIX 標準同樣意義非凡。在多核處理器盛行的今天,多線程編程成為充分利用硬件資源、提高程序性能的關鍵技術。POSIX 標準定義了一套清晰、規范的多線程編程接口,讓開發者可以在不同的操作系統環境中,以統一的方式創建、管理線程,以及處理線程之間的同步和通信問題 。

在計算機編程的廣闊領域中,POSIX 標準就像是一把通用的鑰匙,開啟了跨平臺編程的大門。POSIX,即 Portable Operating System Interface(可移植操作系統接口) ,是 IEEE 為了規范各種 UNIX 操作系統提供的 API 接口而定義的一系列互相關聯標準的總稱。它的出現,旨在解決不同 UNIX 系統之間接口不一致的問題,讓開發者能夠編寫一次代碼,在多個符合 POSIX 標準的系統上運行,實現源代碼級別的軟件可移植性。

對于多線程編程而言,POSIX 標準同樣意義非凡。在多核處理器盛行的今天,多線程編程成為充分利用硬件資源、提高程序性能的關鍵技術。POSIX 標準定義了一套清晰、規范的多線程編程接口,讓開發者可以在不同的操作系統環境中,以統一的方式創建、管理線程,以及處理線程之間的同步和通信問題 。無論是開發高性能的服務器程序,還是優化計算密集型的應用,POSIX 標準下的多線程編程都能提供強大的支持。

接下來,讓我們深入探索 POSIX 標準下的多線程編程世界,揭開線程創建、同步機制等核心概念的神秘面紗。

一、多線程編程簡介

1.1線程初印象

線程,作為進程內的執行單元,可以理解為進程這個大舞臺上的一個個小舞者,各自有著獨立的舞步(執行路徑),卻又共享著舞臺的資源(進程資源)。與進程相比,線程更加輕量級。進程是系統進行資源分配和調度的基本單位,擁有獨立的地址空間、內存、文件描述符等資源 ,進程間的切換開銷較大。而線程則是共享所屬進程的資源,它們之間的切換開銷相對較小,就像在同一個舞臺上不同舞者之間的快速換位,無需重新搭建整個舞臺。

線程的這些特點,使得多線程編程在提升程序執行效率上有著獨特的優勢。多個線程可以并發執行,充分利用多核處理器的并行計算能力,將復雜的任務分解為多個子任務,每個子任務由一個線程負責處理,從而大大提高了程序的整體運行速度。例如,在一個網絡服務器程序中,一個線程可以負責監聽客戶端的連接請求,另一個線程負責處理已經建立連接的客戶端的數據傳輸,這樣可以同時處理多個客戶端的請求,提升服務器的響應性能 。

1.2POSIX 線程庫

在 POSIX 標準下,進行多線程編程離不開 POSIX 線程庫(pthread 庫)。它就像是一根神奇的魔法棒,為開發者提供了一系列強大的接口函數,讓我們能夠輕松地操控線程。

其中,pthread_create函數用于創建一個新的線程 ,它的原型如下:

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

thread參數用于返回新創建線程的 ID;attr參數用于設置線程的屬性,如果為NULL則使用默認屬性;start_routine是一個函數指針,指向線程開始執行時調用的函數;arg是傳遞給start_routine函數的參數。

而pthread_join函數則用于等待一個線程結束,其原型為:

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

thread參數是要等待結束的線程 ID,retval用于獲取線程結束時的返回值。

下面是一個簡單的使用pthread_create和pthread_join函數的代碼示例:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

// 線程執行的函數
void* thread_function(void* arg) {
    printf("線程開始執行,參數為: %s\n", (char*)arg);
    sleep(2);  // 模擬線程執行任務
    printf("線程執行結束\n");
    return (void*)1;  // 返回線程執行結果
}

int main() {
    pthread_t thread;
    int res;
    void* thread_result;

    // 創建線程
    res = pthread_create(&thread, NULL, thread_function, (void*)"Hello, Thread!");
    if (res != 0) {
        perror("線程創建失敗");
        return 1;
    }

    printf("等待線程結束...\n");
    // 等待線程結束,并獲取線程返回值
    res = pthread_join(thread, &thread_result);
    if (res != 0) {
        perror("線程等待失敗");
        return 1;
    }

    printf("線程已結束,返回值為: %ld\n", (long)thread_result);
    return 0;
}

在這個示例中,我們創建了一個新線程,線程執行thread_function函數,在函數中打印傳入的參數,然后休眠 2 秒模擬執行任務,最后返回一個值。主線程通過pthread_join等待子線程結束,并獲取其返回值。

1.3線程的生命周期

線程如同一個有生命的個體,有著自己完整的生命周期,從創建的那一刻開始,經歷運行、阻塞、喚醒等階段,最終走向結束。

當我們調用pthread_create函數時,線程就誕生了,此時它處于就緒狀態,等待著 CPU 的調度。一旦獲得 CPU 時間片,線程就進入運行狀態,開始執行它的任務,也就是調用我們指定的函數 。

在運行過程中,線程可能會因為某些原因進入阻塞狀態。比如,當線程調用sleep函數時,它會主動放棄 CPU 使用權,進入睡眠狀態,直到睡眠時間結束才會重新回到就緒狀態,等待再次被調度執行 。又或者,當線程訪問共享資源時,如果資源被其他線程占用,它就需要等待,從而進入阻塞狀態,直到獲取到資源才會被喚醒,重新進入運行狀態。

當線程執行完它的任務,也就是指定的函數返回時,線程就進入了結束狀態。此時,我們可以通過pthread_join函數等待線程結束,并獲取它的返回值 ,也可以在創建線程時將其設置為分離狀態,這樣線程結束后資源會自動被回收,無需等待。了解線程的生命周期,有助于我們更好地管理線程,優化程序的性能 。

二、Posix網絡API

2.1客戶端和服務端代碼示例

(1)服務端server.cpp

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

int main(int argc,char *argv[])
{
  if (argc != 2)
  {
    printf("Using:./server port\nExample:./server 5005\n\n"); return -1;
  }
  // 第1步:創建服務端的socket。
  int listenfd;
  if ( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) 
  { 
	  perror("socket"); 
	  return -1; 
  }

  // 第2步:把服務端用于通信的地址和端口綁定到socket上。
  struct sockaddr_in servaddr;    // 服務端地址信息的數據結構。
  memset(&servaddr,0,sizeof(servaddr));
  servaddr.sin_family = AF_INET;  // 協議族,在socket編程中只能是AF_INET。
  servaddr.sin_addr.s_addr = htonl(INADDR_ANY);          // 任意ip地址。
  //servaddr.sin_addr.s_addr = inet_addr("192.168.190.134"); // 指定ip地址。
  servaddr.sin_port = htons(atoi(argv[1]));  // 指定通信端口。
  if (bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr)) != 0 )
  { 
	  perror("bind"); 
	  close(listenfd); 
	  return -1; 
  }

  // 第3步:把socket設置為監聽模式。
  if (listen(listenfd,5) != 0 ) 
  { 
	  perror("listen"); 
	  close(listenfd); 
	  return -1; 
  }

  // 第4步:接受客戶端的連接。
  int  clientfd;                  // 連上來的客戶端socket。
  int  socklen = sizeof(struct sockaddr_in); // struct sockaddr_in的大小
  struct sockaddr_in clientaddr;  // 客戶端的地址信息。

  clientfd = accept(listenfd, (struct sockaddr *)&clientaddr, (socklen_t*)&socklen);
  printf("client (%s) connect server success。。。\n", inet_ntoa(clientaddr.sin_addr));

  // 第5步:與客戶端通信,接收客戶端發過來的報文后,將該報文原封不動返回給客戶端。
  char buffer[1024];
  // memset(buffer, 0, 1024);
  while (1)
  {
	    int ret;
	    memset(buffer, 0, sizeof(buffer));
	    // 接收客戶端的請求報文。
	    if ( (ret = recv(clientfd, buffer, sizeof(buffer), 0)) <= 0) 
	    {
	       printf("ret = %d , client disconected!!!\n", ret); 
	       break;   
	    }
	    printf("recv msg: %s\n", buffer);

	    // 向客戶端發送響應結果。
	    if ( (ret = send(clientfd, buffer, strlen(buffer), 0)) <= 0) 
	    { 
		    perror("send"); 
		    break; 
	    }
	    printf("response client: %s success...\n", buffer);

	}
	// 第6步:關閉socket,釋放資源。
	close(listenfd); 
	close(clientfd); 
	return 0;
}

(2)客戶端client.cpp

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

int main(int argc,char *argv[])
{
	if (argc != 3)
	{
		printf("Using:./client ip port\nExample:./client 127.0.0.1 5005\n\n"); return -1;
	}

	// 第1步:創建客戶端的socket。
	int sockfd;
	if ( (sockfd = socket(AF_INET,SOCK_STREAM,0))==-1) 
	{ 
		perror("socket"); 
		return -1; 
	}

	// 第2步:向服務器發起連接請求。
	struct hostent* h;
	if ( (h = gethostbyname(argv[1])) == 0 )   // 指定服務端的ip地址。
	{ printf("gethostbyname failed.\n"); close(sockfd); return -1; }
	struct sockaddr_in servaddr;
	memset(&servaddr,0,sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(atoi(argv[2])); // 指定服務端的通信端口。
	memcpy(&servaddr.sin_addr,h->h_addr,h->h_length);

	// 向服務端發起連接清求。
	if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) != 0)  
	{ 
		perror("connect"); 
		close(sockfd); 
		return -1; 
	}

	char buffer[1024];

	// 第3步:與服務端通信,發送一個報文后等待回復,然后再發下一個報文。
	for (int i = 0; i < 3; i++)
	{
		int ret;
		memset(buffer, 0, sizeof(buffer));
		sprintf(buffer, "這是第[%d]條消息!", i+1);
		if ( (ret = send(sockfd, buffer, strlen(buffer),0)) <= 0) // 向服務端發送請求報文。
		{ 
			perror("send"); 
			break; 
		}
		printf("發送:%s\n", buffer);

		memset(buffer,0,sizeof(buffer));
		if ( (ret = recv(sockfd, buffer, sizeof(buffer), 0)) <= 0) // 接收服務端的回應報文。
		{
			printf("ret = %d error\n", ret); 
			break;
		}
		printf("從服務端接收:%s\n", buffer);
		sleep(1);
	}

	// 第4步:關閉socket,釋放資源。
	close(sockfd);
}

運行結果:

圖片圖片

著重分析以下幾個函數

(1)socket函數

int socket(int domain, int type, int protocol);

調用socket()函數會創建一個套接字(socket)對象。套接字由兩部分組成,文件描述符(fd)和 TCP控制塊(Tcp Control Block,tcb) 。Tcb主要包括關系信息有網絡的五元組(remote IP,remote Port, local IP, local Port, protocol),一個五元組就可以確定一個具體的網絡連接。

(2)listen函數

listen(int listenfd, backlog);

服務端在調用listen()后,就開始監聽網絡上連接請求。第二個參數 backlog, 在Linux是指全連接隊列的長度,即一次最多能保存 backlog 個連接請求。

圖片圖片

(3)connect 函數

客戶端調用connect()函數,向指定服務端發起連接請求。

(4)accept 函數

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

accept()函數只做兩件事,將連接請求從全連接隊列中取出,給該連接分配一個fd并返回。

(5) 三次握手過程分析

三次握手與listen/connect/accept三個函數有關,這里放到一起進行描述。

客戶端調用 connect 函數,開始進入三次握手。客戶端發送syn包,以及帶著隨機的seq;

服務端listen函數監聽到有客戶端連接,listen函數會在內核協議棧為該客戶端創建一個Tcb控制塊,并將其加入到半連接隊列。服務端在收到syn包后,會給客戶端恢復ack和syn包;

客戶端收到服務端的ack和syn后再次恢復ack,連接建立成功。

服務端在收到客戶端的ack后,會將該客戶端對應的Tcb數據從半連接隊列移動到全連接隊列。只要全連接隊列中有數據就會觸發accept,返回連接成功的客戶端fd、IP以及端口。此時,Tcb完整的五元組構建成功。

(6)send/recv 函數

至此,客戶端與服務端已經成功建立連接,就可以相互通信了。

send/recv函數主要負責數據的收發。

過程分析

send函數:負責將數據從用戶空間拷貝到內核(具體是拷貝到該連接對應的Tcb控制塊中的發送緩沖區)。注意:send函數返回并不意味著數據已成功發送,因為數據在到達內核緩沖區后,內核會根據自己的策略決定什么時候將數據發出。

recv函數:負責將數據從內核緩沖區拷貝到用戶空間。同理,數據也顯示到達該連接對應的Tcb控制塊的接受緩沖區。

(7)close 函數

在服務器與客戶端建立連接之后,會進行一些讀寫操作,完成讀寫操作后我們需要關閉相應的socket,好比操作完打開的文件要調用fclose關閉打開的文件一樣。close過程涉及到四次揮手的全過程

四次揮手流程:

  • 客戶端調用close函數,內核會發送fin包,客戶端進入fin_wait1狀態;
  • 服務端收到fin包回復ack,客戶端進入close_wait狀態。此時,客戶客戶端往服務端發送的通道就關閉了,因為Tcp是全雙工的,服務端還可以向客戶端發數據。
  • 客戶端收到ack,進入到fin_wait2狀態;
  • 服務端發送完數據,發送fin包,服務端進入last_ack狀態;
  • 客戶端收到fin包后,回復ack,進入到time_wait狀態;
  • 服務端收到ack,雙方連接正常關閉。

注意:close操作只是讓相應socket描述字的引用計數-1,只有當引用計數為0的時候,才會觸發TCP客戶端向服務器發送終止連接請求

2.2雙方同時調用close

圖片圖片

2.3常見面試問題

為什么要三次握手?

答:因為一個完整的TCP連接需要雙方都得到確認,客戶端發送請求和收到確認需要兩次;服務端發送請求和收到確認需要兩次,當中服務回復確認和發送請求合并為一次總共需要3次;才能保證雙向通道是通的。

一個服務器的端口數是65535,為何能做到一百萬的連接?

答:主要是因為一條連接是由五元組所組成,所以一個服務器的連接數是五個成員數的乘積。

如何應對Dos(Deny of Service,拒絕服務)攻擊?

答:Dos攻擊就是利用三次握手的原理,模擬客戶端只向服務器發送syn包,然后耗盡被攻擊對象的資源。比較多的做法是利用防火墻,做一些過濾規則

如何解決Tcp的粘包問題?

答:(1) 在包頭上添加一個數據包長度的字段,用于數據的劃分,實際項目中這個也用的最多;(2)包尾部加固定分隔符;

Tcp如何保證順序到達?

答:順序到達是由于TCP的延遲ACK的機制來保證的,TCP接收到數據并不是立即回復而是經過一個延遲時間,回復接收到連續包的最大序列號加1。如果丟包之后的包都需要重傳。在弱網情況下這里就會有實時性問題和帶寬占用的問題;

time_wait 作用?

答:防止最后一個ACK沒有順利到達對方,超時重新發送ack。time_wait時常一般是120s可以修改。

服務器掉線重啟出現端口被占用怎么辦?

答:其實主要是由于還處于time_wait狀態,端口并沒有真正釋放。這時候可以設置SO_REUSEADDR屬性,保證掉線能馬上重連。

三、同步機制:多線程協作的 “指揮家”

在多線程編程的舞臺上,同步機制就像是一位經驗豐富的指揮家,協調著各個線程的行動,確保它們能夠和諧共處,高效地完成任務。多線程編程中,由于多個線程共享進程資源,資源競爭和線程協作問題不可避免,而同步機制正是解決這些問題的關鍵。接下來,我們將深入探討互斥鎖、信號量和條件變量這幾種常見的同步機制 。

3.1資源競爭:多線程中的 “暗礁”

當多個線程同時訪問和修改共享資源時,資源競爭問題就如同隱藏在暗處的暗礁,隨時可能讓程序的運行陷入混亂。假設我們有一個簡單的程序,包含兩個線程,它們都試圖對一個全局變量進行加 1 操作:

#include <stdio.h>
#include <pthread.h>

// 全局變量
int global_variable = 0;

// 線程執行函數
void* thread_function(void* arg) {
    for (int i = 0; i < 1000000; i++) {
        global_variable++;
    }
    return NULL;
}

int main() {
    pthread_t thread1, thread2;

    // 創建線程
    pthread_create(&thread1, NULL, thread_function, NULL);
    pthread_create(&thread2, NULL, thread_function, NULL);

    // 等待線程結束
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    printf("最終的全局變量值: %d\n", global_variable);
    return 0;
}

按照我們的預期,兩個線程各對全局變量加 1000000 次,最終的結果應該是 2000000。然而,實際運行這個程序,你會發現結果往往小于 2000000。這是因為在多線程環境下,global_variable++ 這一操作并非原子操作,它實際上包含了讀取變量值、加 1、寫回變量值這三個步驟 。當兩個線程同時執行這一操作時,可能會出現一個線程讀取了變量值,還未完成加 1 和寫回操作,另一個線程也讀取了相同的值,導致最終結果出現偏差,數據不一致 。

3.2互斥鎖:守護資源的 “衛士”

互斥鎖(Mutex)是解決資源競爭問題的常用工具,它就像一位忠誠的衛士,守護著共享資源,確保同一時間只有一個線程能夠訪問資源。互斥鎖的工作原理基于一個簡單的概念:當一個線程獲取到互斥鎖時,其他線程就必須等待,直到該線程釋放互斥鎖。

在 POSIX 線程庫中,使用互斥鎖非常簡單。首先,我們需要定義一個互斥鎖變量:

pthread_mutex_t mutex;

然后,在訪問共享資源之前,通過 pthread_mutex_lock 函數獲取互斥鎖:

pthread_mutex_lock(&mutex);

如果互斥鎖已經被其他線程持有,調用 pthread_mutex_lock 的線程將被阻塞,直到互斥鎖被釋放。當訪問完共享資源后,使用 pthread_mutex_unlock 函數釋放互斥鎖:

pthread_mutex_unlock(&mutex);

下面是使用互斥鎖改進后的代碼:

#include <stdio.h>
#include <pthread.h>

// 全局變量
int global_variable = 0;
// 互斥鎖
pthread_mutex_t mutex;

// 線程執行函數
void* thread_function(void* arg) {
    for (int i = 0; i < 1000000; i++) {
        // 獲取互斥鎖
        pthread_mutex_lock(&mutex);
        global_variable++;
        // 釋放互斥鎖
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

int main() {
    pthread_t thread1, thread2;

    // 初始化互斥鎖
    pthread_mutex_init(&mutex, NULL);

    // 創建線程
    pthread_create(&thread1, NULL, thread_function, NULL);
    pthread_create(&thread2, NULL, thread_function, NULL);

    // 等待線程結束
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    // 銷毀互斥鎖
    pthread_mutex_destroy(&mutex);

    printf("最終的全局變量值: %d\n", global_variable);
    return 0;
}

通過這種方式,互斥鎖有效地保護了共享資源,確保了數據的一致性 。

3.3信號量:資源分配的 “調度員”

信號量(Semaphore)是另一種強大的同步工具,它不僅可以用于實現互斥,還能用于管理資源的分配。信號量可以看作是一個計數器,它的值表示可用資源的數量 。當一個線程想要訪問資源時,它需要先獲取信號量,如果信號量的值大于 0,則表示有可用資源,線程可以獲取信號量并繼續執行,同時信號量的值減 1;如果信號量的值為 0,則表示沒有可用資源,線程將被阻塞,直到有其他線程釋放信號量 。

在 POSIX 標準中,信號量相關的函數主要有 sem_init(初始化信號量)、sem_wait(等待信號量)、sem_post(釋放信號量)和 sem_destroy(銷毀信號量)。假設我們有一個場景,有多個線程需要訪問有限數量的資源,比如數據庫連接池中的連接。我們可以使用信號量來控制對這些資源的訪問:

#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>

// 定義信號量,假設有5個可用資源
sem_t semaphore;

// 線程執行函數
void* thread_function(void* arg) {
    // 等待信號量
    sem_wait(&semaphore);
    printf("線程獲取到資源,開始執行任務...\n");
    // 模擬任務執行
    sleep(1);
    printf("線程任務執行完畢,釋放資源\n");
    // 釋放信號量
    sem_post(&semaphore);
    return NULL;
}

int main() {
    pthread_t threads[10];

    // 初始化信號量,設置初始值為5
    sem_init(&semaphore, 0, 5);

    // 創建10個線程
    for (int i = 0; i < 10; i++) {
        pthread_create(&threads[i], NULL, thread_function, NULL);
    }

    // 等待所有線程結束
    for (int i = 0; i < 10; i++) {
        pthread_join(threads[i], NULL);
    }

    // 銷毀信號量
    sem_destroy(&semaphore);

    return 0;
}

在這個例子中,我們初始化信號量的值為 5,表示有 5 個可用資源。每個線程在執行任務前先通過 sem_wait 等待信號量,獲取到信號量后才能訪問資源,執行完任務后通過 sem_post 釋放信號量,這樣就保證了同時最多只有 5 個線程可以訪問資源 。

3.4條件變量:線程間的 “傳聲筒”

條件變量(Condition Variable)用于線程間基于條件的通信,它為線程提供了一種等待特定條件發生的機制,就像一個傳聲筒,讓線程之間能夠相互傳達信息。條件變量通常與互斥鎖配合使用,以實現線程之間的同步和協作。

一個經典的例子是生產者 - 消費者模型。在這個模型中,生產者線程負責生成數據并將其放入緩沖區,消費者線程則從緩沖區中取出數據進行處理。當緩沖區為空時,消費者線程需要等待,直到生產者線程向緩沖區中放入數據;當緩沖區滿時,生產者線程需要等待,直到消費者線程從緩沖區中取出數據 。

下面是使用條件變量和互斥鎖實現生產者 - 消費者模型的代碼示例:

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>

#define BUFFER_SIZE 5
int buffer[BUFFER_SIZE];
int in = 0, out = 0;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t not_empty = PTHREAD_COND_INITIALIZER;
pthread_cond_t not_full = PTHREAD_COND_INITIALIZER;

// 生產者線程函數
void* producer(void* arg) {
    while (1) {
        int item = rand() % 100; // 生成一個隨機數
        pthread_mutex_lock(&mutex);
        while ((in + 1) % BUFFER_SIZE == out) { // 緩沖區滿
            pthread_cond_wait(?_full, &mutex);
        }
        buffer[in] = item;
        printf("生產者放入數據: %d\n", item);
        in = (in + 1) % BUFFER_SIZE;
        pthread_cond_signal(?_empty);
        pthread_mutex_unlock(&mutex);
        sleep(rand() % 2); // 模擬生產時間
    }
    return NULL;
}

// 消費者線程函數
void* consumer(void* arg) {
    while (1) {
        pthread_mutex_lock(&mutex);
        while (in == out) { // 緩沖區空
            pthread_cond_wait(?_empty, &mutex);
        }
        int item = buffer[out];
        printf("消費者取出數據: %d\n", item);
        out = (out + 1) % BUFFER_SIZE;
        pthread_cond_signal(?_full);
        pthread_mutex_unlock(&mutex);
        sleep(rand() % 3); // 模擬消費時間
    }
    return NULL;
}

int main() {
    pthread_t producer_thread, consumer_thread;

    // 創建生產者和消費者線程
    pthread_create(&producer_thread, NULL, producer, NULL);
    pthread_create(&consumer_thread, NULL, consumer, NULL);

    // 等待線程結束
    pthread_join(producer_thread, NULL);
    pthread_join(consumer_thread, NULL);

    // 銷毀互斥鎖和條件變量
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(?_empty);
    pthread_cond_destroy(?_full);

    return 0;
}

在這個代碼中,pthread_cond_wait 函數會使線程進入等待狀態,并自動釋放互斥鎖,當條件滿足被喚醒時,會重新獲取互斥鎖。pthread_cond_signal 函數則用于喚醒等待在條件變量上的一個線程。通過條件變量和互斥鎖的緊密配合,生產者和消費者線程能夠有條不紊地工作,實現高效的數據處理 。

四、多線程編程實戰演練

4.1多線程案例分析

在日常的編程工作中,文件處理是一項常見的任務。當面對大量文件需要處理時,單線程的處理方式往往效率低下,而多線程編程則能成為提升效率的利器。假設我們有一個需求:處理一批日志文件,需要統計每個文件中特定關鍵詞出現的次數,并將結果匯總。

為了實現這個目標,我們可以設計一個多線程的文件處理方案。首先,將文件列表進行分割,把不同的文件分配給不同的線程處理,這就像是將一堆任務分配給不同的工人,每個工人專注于自己手頭的任務 。每個線程負責讀取分配給自己的文件內容,逐行掃描,統計關鍵詞出現的次數。

這個過程中,線程之間的同步機制至關重要。我們可以使用互斥鎖來保護共享的統計結果變量,確保不同線程在更新統計結果時不會出現數據競爭問題 。比如,當一個線程統計完自己負責文件后,需要將統計結果累加到全局的統計變量中,此時通過獲取互斥鎖,保證同一時間只有一個線程能夠進行累加操作,避免了數據不一致的情況 。

4.2代碼實現示例

下面是使用 POSIX 線程庫實現多線程文件處理的具體代碼:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>

#define MAX_FILES 10
#define KEYWORD "error"  // 要統計的關鍵詞

// 線程參數結構體
typedef struct {
    char *file_name;
} ThreadArgs;

// 全局統計變量
int global_count = 0;
// 互斥鎖
pthread_mutex_t mutex;

// 線程執行函數
void* count_keyword(void* arg) {
    ThreadArgs *args = (ThreadArgs*)arg;
    FILE *file = fopen(args->file_name, "r");
    if (file == NULL) {
        perror("文件打開失敗");
        pthread_exit(NULL);
    }

    char line[1024];
    int local_count = 0;
    while (fgets(line, sizeof(line), file) != NULL) {
        if (strstr(line, KEYWORD) != NULL) {
            local_count++;
        }
    }
    fclose(file);

    // 獲取互斥鎖,更新全局統計變量
    pthread_mutex_lock(&mutex);
    global_count += local_count;
    pthread_mutex_unlock(&mutex);

    pthread_exit(NULL);
}

int main() {
    pthread_t threads[MAX_FILES];
    ThreadArgs args[MAX_FILES];
    char file_names[MAX_FILES][50] = {"file1.log", "file2.log", "file3.log", "file4.log", "file5.log", "file6.log", "file7.log", "file8.log", "file9.log", "file10.log"};

    // 初始化互斥鎖
    pthread_mutex_init(&mutex, NULL);

    // 創建線程并分配文件
    for (int i = 0; i < MAX_FILES; i++) {
        args[i].file_name = file_names[i];
        if (pthread_create(&threads[i], NULL, count_keyword, &args[i]) != 0) {
            perror("線程創建失敗");
            return 1;
        }
    }

    // 等待所有線程結束
    for (int i = 0; i < MAX_FILES; i++) {
        if (pthread_join(threads[i], NULL) != 0) {
            perror("線程等待失敗");
            return 1;
        }
    }

    // 銷毀互斥鎖
    pthread_mutex_destroy(&mutex);

    printf("關鍵詞 '%s' 出現的總次數: %d\n", KEYWORD, global_count);
    return 0;
}

在這段代碼中,count_keyword 函數是線程執行的主體,它打開分配的文件,逐行讀取并統計關鍵詞出現的次數,最后通過互斥鎖將本地統計結果累加到全局變量中 。main 函數負責創建線程,為每個線程分配文件,并等待所有線程執行完畢后輸出最終的統計結果 。

4.3多線程調試與優化

在多線程程序的調試過程中,我們可能會遇到各種各樣的問題。死鎖是一個常見的問題,比如兩個線程分別持有不同的鎖,卻又試圖獲取對方持有的鎖,就會陷入死鎖狀態,導致程序無法繼續執行 。為了檢測死鎖,可以使用工具如Valgrind的Helgrind工具,它能夠幫助我們發現潛在的死鎖問題。一旦發現死鎖,我們需要仔細檢查代碼中鎖的獲取和釋放順序,避免嵌套鎖的不合理使用 。

線程異常也是需要關注的問題。當線程執行過程中出現未捕獲的異常時,可能會導致整個程序崩潰。我們可以在線程函數中使用try - catch塊(如果是 C++ 代碼)或者進行適當的錯誤處理,確保線程在遇到異常時能夠安全地退出,而不影響其他線程的正常運行 。

在優化方面,合理調整線程數量是一個重要的思路。線程數量并非越多越好,過多的線程會導致上下文切換開銷增大,反而降低程序性能 。對于 CPU 密集型的任務,線程數量可以設置為接近 CPU 核心數;對于 I/O 密集型的任務,由于線程在等待 I/O 操作時會阻塞,不會占用 CPU 資源,因此可以適當增加線程數量 。此外,優化同步機制也能提升性能,比如使用更細粒度的鎖,減少鎖的競爭范圍,或者在合適的場景下使用無鎖數據結構,避免鎖帶來的開銷 。通過不斷地調試和優化,我們能夠讓多線程程序更加穩健高效地運行 。

責任編輯:武曉燕 來源: 深度Linux
相關推薦

2022-07-15 08:16:56

Stream函數式編程

2024-04-02 11:43:08

向量化編程NEON

2023-12-15 15:55:24

Linux線程同步

2018-10-08 15:22:36

IO模型

2024-04-12 12:19:08

語言模型AI

2022-03-24 08:51:48

Redis互聯網NoSQL

2022-04-11 10:56:43

線程安全

2019-11-13 09:27:55

Web密碼學數據

2025-04-30 08:15:00

Linux系統進程

2023-09-15 12:00:01

API應用程序接口

2023-09-08 08:20:46

ThreadLoca多線程工具

2021-03-22 10:05:59

netstat命令Linux

2024-02-02 18:29:54

C++線程編程

2023-11-03 12:29:48

Java虛擬線程

2022-05-05 16:47:24

Docker網絡空間容器

2025-04-28 02:22:00

2020-05-15 16:37:13

PowerBI數據分析

2023-07-04 08:56:07

指針類型Golang

2023-04-03 15:04:00

RPCPHP語言

2023-08-24 16:50:45

點贊
收藏

51CTO技術棧公眾號

怡红院av亚洲一区二区三区h| 91亚洲精品一区| 亚洲乱码国产乱码精品精大量| 都市激情亚洲一区| 中文字幕一区二区三区乱码在线| 亚洲自拍偷拍色图| 日韩欧美亚洲视频| 欧美电影一二区| 亚洲国产精品va在线看黑人动漫 | 国产主播福利在线| 激情五月婷婷综合网| 久久99国产综合精品女同| 51调教丨国产调教视频| 亚洲人成777| 精品123区| 粉嫩一区二区三区四区公司1| 午夜亚洲福利老司机| 日韩一区国产在线观看| 亚洲精品一级片| 日韩av午夜在线观看| 久久久久亚洲精品| 欧美肥妇bbwbbw| 午夜欧洲一区| 欧美成va人片在线观看| 中国黄色片免费看| 在线观看欧美日韩电影| 亚洲午夜久久久久久久久电影院 | 凹凸成人精品亚洲精品密奴| 精品av综合导航| 色18美女社区| 国产福利亚洲| 色综合一个色综合亚洲| 国产精品无码电影在线观看| yjizz视频网站在线播放| 成人av在线一区二区三区| 成人乱色短篇合集| 男人天堂视频网| 国产精品入口66mio| 欧美富婆性猛交| 91久久国产综合| 日韩精品一区二区三区免费观看| 日韩不卡在线观看| 影音先锋资源av| 2020国产精品极品色在线观看| 正在播放亚洲一区| 国产一级片自拍| 超碰这里只有精品| 在线亚洲免费视频| 无码人妻丰满熟妇区毛片| 97人澡人人添人人爽欧美| 亚洲综合无码一区二区| 欧美人与动牲交xxxxbbbb| 黄色av电影在线播放| 国产精品久久福利| 亚洲视频在线观看日本a| yourporn在线观看中文站| 国产欧美日韩精品a在线观看| 欧美伦理一区二区| 男人天堂综合| 国产欧美一区二区三区在线看蜜臀| 九色一区二区| 国产综合视频一区二区三区免费| 久久日一线二线三线suv| 欧美少妇一区| 成年在线电影| 亚洲欧洲日韩一区二区三区| 一个色的综合| 米奇777四色精品人人爽| 亚洲欧洲国产日韩| 中国老女人av| 1024在线看片你懂得| 亚洲国产精品久久人人爱蜜臀| 国产免费一区二区视频| 综合另类专区| 欧美日韩一卡二卡三卡| 欧美日韩理论片| 欧美黄视频在线观看| 亚洲成人在线网| 久久国产精品无码一级毛片| 国产探花在线精品| 久久精品成人动漫| 99免费在线观看| 久久久久久久尹人综合网亚洲| 国产精品海角社区在线观看| 88av在线视频| 成人h动漫精品一区二区| 久久精品成人一区二区三区蜜臀| 国产区在线视频| 亚洲日本青草视频在线怡红院 | 男人天堂资源网| 亚洲h色精品| 97超碰色婷婷| 91亚洲欧美激情| 成人网男人的天堂| 欧美一区二区高清在线观看| 操你啦视频在线| 亚洲不卡一区二区三区| 国产又大又黄又粗的视频| 91成人app| 亚洲护士老师的毛茸茸最新章节| 日本二区在线观看| 欧美福利电影在线观看| 日本道色综合久久影院| 国产免费不卡av| 91色|porny| 久久综合亚洲精品| 亚洲不卡系列| 亚洲大胆人体av| 成年人免费视频播放| 在线看福利67194| 男女羞羞免费视频| 久久精品一区| 99在线观看| av电影在线观看一区二区三区| 亚洲电影第三页| 奇米视频7777| 国产va免费精品观看精品视频 | 美国精品一区二区| 亚洲视频成人| 国产成人精品免费视频大全最热| yiren22亚洲综合伊人22| 午夜欧美大尺度福利影院在线看| 天堂av在线网站| 天海翼精品一区二区三区| 欧美成人免费一级人片100| 无码人妻一区二区三区线| 成人免费黄色大片| 日本黄网站色大片免费观看| 视频精品导航| 亚洲欧洲偷拍精品| 久久久国产高清| 国产高清亚洲一区| 在线视频91| 成人精品一区二区三区电影| 亚洲精品日韩欧美| 日韩免费一级片| 国产丶欧美丶日本不卡视频| 在线看成人av电影| 欧美日韩尤物久久| 国产一区二区av| 天天干在线播放| 99re这里都是精品| 欧美日韩精品在线一区二区| 国产一区二区三区不卡av| 九九热精品在线| av中文字幕第一页| 亚洲人成网站在线| 亚洲欧美偷拍另类| 偷偷www综合久久久久久久| 国产欧美最新羞羞视频在线观看| 麻豆国产在线播放| 色呦呦国产精品| 国产成人av一区二区三区不卡| 亚洲永久字幕| 欧美一区二区三区四区五区六区| 天堂中文最新版在线中文| 亚洲精品综合久久中文字幕| 亚洲男人的天堂在线视频| 91免费视频大全| 北条麻妃在线视频| 日韩欧美高清在线播放| 91精品视频在线看| a黄色片在线观看| 精品免费一区二区三区| 69精品久久久| 久久夜色精品国产噜噜av| 精品久久久久久久免费人妻| 欧洲三级视频| 成人中文字幕在线观看| 日本无删减在线| 亚洲国产高清自拍| 国产免费一区二区三区四区五区| 国产免费观看久久| 久久久久xxxx| 亚洲视频中文| 欧美精品一区二区三区在线看午夜| 国模套图日韩精品一区二区| 中文字幕无线精品亚洲乱码一区 | 国模私拍一区二区三区| 日韩黄色影片| 欧美日韩不卡一区二区| 看片网站在线观看| 91女厕偷拍女厕偷拍高清| 国产又猛又黄的视频| 欧美精品首页| 免费h精品视频在线播放| 国产精品天堂蜜av在线播放 | 日韩电影在线播放| 欧美日韩午夜电影网| 91精品国产一区| 在线免费观看的av网站| 欧美xxx久久| 波多野结衣网站| 一区二区三区四区激情| 久久精品国产亚洲av久| 国产精品一区二区黑丝| 国产成人黄色片| 99精品小视频| 久久精品国产99精品国产亚洲性色| 99蜜月精品久久91| 久久久久一本一区二区青青蜜月| 国产毛片av在线| 精品1区2区在线观看| 一级黄色小视频| 无码av中文一区二区三区桃花岛| 美国美女黄色片| 不卡视频在线看| 毛片毛片毛片毛| 欧美中文日韩| 国产中文字幕乱人伦在线观看| 欧美一区电影| 久久狠狠久久综合桃花| 日本在线成人| 91精品国产综合久久男男| 欧美13videosex性极品| 欧美区二区三区| 91伦理视频在线观看| 亚洲精品动漫100p| 性猛交xxxx乱大交孕妇印度| 欧美日韩日本视频| 黄色片中文字幕| 亚洲国产日韩在线一区模特| 中文字幕求饶的少妇| 久久精品视频在线看| 中文字幕a在线观看| 激情综合亚洲精品| jizz大全欧美jizzcom| 免费一区视频| 国产欧美日韩网站| 国产精品久久| 日韩中文字幕亚洲精品欧美| 婷婷综合视频| 一区高清视频| 欧美电影免费播放| 亚洲一卡二卡三卡| 欧美日一区二区| 日产中文字幕在线精品一区| 亚洲另类春色校园小说| 精品国产一区二区三区日日嗨| 91综合久久爱com| dy888夜精品国产专区| 日本免费精品| 国产福利久久精品| 国产乱人伦精品一区| 成人情视频高清免费观看电影| 日本一区二区三区电影免费观看| 91午夜理伦私人影院| 亚洲午夜国产成人| 91在线无精精品一区二区| 一区二区三区| 亚洲最大成人网色| 成人精品动漫一区二区三区| 超碰97在线资源| 国产一区福利| 久热这里只精品99re8久 | 粉嫩一区二区三区在线看| 亚洲国产日韩在线一区| 国产v综合v亚洲欧| 香港三日本8a三级少妇三级99| 99这里都是精品| www.自拍偷拍| 国产精品美日韩| 国产suv精品一区二区68| 一区二区三区日韩欧美精品| 日本学生初尝黑人巨免费视频| 午夜成人免费电影| 黄色av网站免费观看| 欧美性高清videossexo| 国产精品久久久久毛片| 欧美成人官网二区| 日韩国产福利| 精品国产一区二区三区四区在线观看| av文字幕在线观看| 欧美精品www| 精品91久久| 成人免费直播live| 国产精品毛片av| 午夜精品一区二区在线观看的| 91亚洲自偷观看高清| 国产传媒久久久| 久久久精品日韩| 亚洲免费av一区| 成人黄色在线视频| 内射毛片内射国产夫妻| 亚洲最新在线观看| 狠狠狠狠狠狠狠| 欧美一区二区三区播放老司机| 色综合视频在线| 国产精品18久久久久久久久| 97视频在线播放| 免费在线观看的电影网站| 欧美性视频网站| 99精品国产九九国产精品| 成人在线观看91| 欧美日韩一二三四| 国产96在线 | 亚洲| 日韩主播视频在线| av不卡中文字幕| 国产精品国产三级国产有无不卡| 免费人成年激情视频在线观看| 一本色道久久综合亚洲91| 国产情侣激情自拍| 亚洲欧洲在线观看| av网站大全在线| 国产精品99久久久久久久久| 成人中文字幕视频| 亚洲精品在线免费看| 亚洲精品日韩久久| 欧美大片久久久| 久久久久久久久久电影| 久草免费新视频| 欧美精品在欧美一区二区少妇| 天天操天天插天天射| 久久亚洲欧美日韩精品专区| 成人激情综合| 精品在线不卡| 国产精品久久| 无套白嫩进入乌克兰美女| 欧美国产综合一区二区| 国产精品午夜影院| 日韩精品一区二区三区三区免费| 在线国产91| 国产成人拍精品视频午夜网站| 国产精品网址| 日本手机在线视频| 国产高清不卡二三区| 特黄一区二区三区| 欧美午夜免费电影| 国产乱理伦片a级在线观看| 91国内精品久久| 久久成人福利| 丰满少妇大力进入| 国产成人亚洲精品青草天美 | 中文字幕日韩欧美一区二区三区| 国产一区二区视频免费| 日韩电影大全免费观看2023年上| 丝袜综合欧美| 亚洲一区二区三区sesese| 99热在线成人| 日本黄色福利视频| 国产精品久久久久久户外露出| 久久午夜鲁丝片| 在线观看亚洲视频| www.一区| 中国成人在线视频| 裸体一区二区三区| 日本中文不卡| 在线一区av| 久久婷婷人人澡人人喊人人爽| 伊人久久久大香线蕉综合直播 | 亚洲无码精品一区二区三区| 日韩精品极品在线观看播放免费视频| 91高清视频在线观看| 国产精品久久久久久久久久久久冷| 欧美99在线视频观看| 成年人看片网站| 午夜电影一区二区| 欧美日韩激情视频一区二区三区| 欧美一级大胆视频| 国产探花在线精品| 91亚洲精品久久久蜜桃借种| 亚洲天堂网中文字| 性生交大片免费看女人按摩| 欧美激情一区二区三区成人| 国产一区调教| 亚洲人成无码www久久久| 国产婷婷色一区二区三区在线| 午夜精品久久久久久久蜜桃| 中文字幕在线看视频国产欧美在线看完整 | 粉嫩老牛aⅴ一区二区三区| 天天av综合网| 国产精品三级久久久久久电影| 99精品小视频| 欧美久久久久久久久久久| 午夜久久电影网| 黄色视屏网站在线免费观看| 国产一区二区在线免费| 欧美视频久久| 巨胸大乳www视频免费观看| 欧美日韩综合在线| 日本一本在线免费福利| 欧美精品欧美精品系列c| 欧美日韩经典丝袜| 亚洲成色www8888| 在线一区av| 中文字幕综合在线观看| 成人福利视频网站| 久久久久久av无码免费看大片| 久久精品欧美视频| 青草久久视频| 亚洲怡红院在线| 性久久久久久久久久久久| 91电影在线播放| 国产欧美日韩视频一区二区三区| 久久精品免费| 欧美日韩在线国产| 国产亚洲一区二区精品| 伊人精品久久| 三上悠亚在线一区二区| 亚洲国产裸拍裸体视频在线观看乱了 |