當前位置:首頁 » 編程軟體 » linux多線程編程實例

linux多線程編程實例

發布時間: 2022-05-01 09:06:19

1. linux多線程小程序

首先聲明我也不是高手,我和你一樣,正在學習可以討論、討論,我說的只是自己的理解,不一定完全正確!

你使用信號量的作用是做同步的如果operate_data1或者 operate_data2線程先運行的話他們不會先去作運算的,不會出現你說的那種「當數沒全寫到數組里,就進行運算了」的情況,因為在他們運算之前調用了sem_wait(&s);首先要有信號資源可用才可以去運算,而現在只有3種情況可以獲得信號資源:

1、初始化信號量的時候是正值,顯然你的不是 sem_init(&s,0,0);
2、void write_data1(int *a)線程運行後;
3、void write_data1(int *a)線程運行後;

只有以上這三種情況的任意一種發生了,兩個operate_data中的一個才可能獲得資源而運行。要解決這種情況最簡單的情況是把兩個寫數據操作放到一個線程中把兩個操作數據放到一個線程中,這樣就不會出現競爭的情況了。或是在把兩個寫數據操作放到一個線程中,在操作線程中,在操作前sem_wait(&s);在操作後調用sem_post(&s);

2. linux 編寫一個多線程程序,要求主線程創建3個子線程,3個子線程在執行時都修改一個他們的共享變數。

void func1(int n)
{
printf("%d",n*10);
}
void func1(int n)
{
printf("%d",n-10);
}

void func1(int n)
{
printf("%d",n/2);
}

int main(void)
{
int n = 10;
pthread_t 1_thread,2_thread,3_thread;
pthread_create(1_thread,NULL,func1,n);
pthread_create(2_thread,NULL,func2,n);
pthread_create(3_thread,NULL,func3,n);
return 0;
}

3. linux下多進程或者多線程編程的問題。新手,望指教!

你好,多進程或多線程,都不會阻塞當前語句代碼。為了您的理解,我就大膽舉下面兩個例子:
多進程:你可以看成是本來是一條路的,現在從中間拆成兩條,然後每一條路都有屬於自己這條路的代碼在運行。
多線程:你可以看成是一條路,然後分出車道,比如左車道和右車道甚至是停車道,然後每條車道都單獨通車,其他車道的不能對這條車道進行干擾。

所以,把一條路從中間拆成兩條,成本是很高的。但是把一條路分車道,成本就不是很高了。
對於您提出的main函數的疑問,當main函數最後執行完畢,程序退出後,所有的進程包括線程,都會被關閉的,哪怕你的程序中沒有關閉,操作系統也會幫你關閉的,現在的操作系統都非常的完善了。當然,也存在有線程或進程不被釋放的特殊情況,最好在編程中要記得釋放。

4. linux下多線程編程問題,求各種優化

while((p=fork())==-1);//創建進程
if(p==0)
{
ret=pthread_create(&id1,NULL,(void *)pthread1, NULL);//創建線程
if(ret!=0) perror("線程1創建失敗");
ret=pthread_create(&id2,NULL,(void *)pthread2, NULL);
if(ret!=0) perror("線程2創建失敗");
ret=pthread_create(&id3,NULL,(void *)pthread3, NULL);
if(ret!=0) perror("線程3創建失敗");

......
pthread_join(id1,NULL);
pthread_join(id2,NULL);
pthread_join(id3,NULL);
..........//結束線程
exit(0);
}

void pthread1(void *arg)
{
while(time(NULL) < time_flag) //可以在這里判斷總接入次數,現在是時間判斷
{
if(pthread_mutex_lock(&mutex)!=0)//鎖定數據 可以在這里判斷接入次數,現在是寫鎖定判斷
{
perror("鎖定失敗");
}
else printf("線程1:鎖定數據量\n");
{

}
if(pthread_mutex_unlock(&mutex)!=0) //數據解鎖 這里可以判斷不超過3次鎖定解鎖
{
perror("解鎖失敗");
}
else
printf("線程1:我已解鎖\n");
sleep(4);
}
}
其他的你自己補充吧,自己定義幾個全局變數控制線程鎖定解鎖邏輯關系就行

5. 嵌入式Linux應用層開發有哪些實例

一:C語言 嵌入式Linux工程師的學習需要具備一定的C語言基礎,C語言是嵌入式領域最重要也是最主要的編程語言,通過大量編程實例重點理解C語言的基礎編程以及高級編程知識。包括:基本數據類型、數組、指針、結構體、鏈表、文件操作、隊列、棧等。
二:Linux基礎 Linux操作系統的概念、安裝方法,詳細了解Linux下的目錄結構、基本命令、編輯器VI ,編譯器GCC,調試器GDB和 Make 項目管理工具, Shell Makefile腳本編寫等知識,嵌入式開發環境的搭建。
三:Linux系統編程 重點學習標准I/O庫,Linux多任務編程中的多進程和多線程,以及進程間通信(pipe、FIFO、消息隊列、共享內存、signal、信號量等),同步與互斥對共享資源訪問控制等重要知識,主要提升對Linux應用開發的理解和代碼調試的能力。
四:Linux網路編程 計算機網路在嵌入式Linux系統應用開發過程中使用非常廣泛,通過Linux網路發展、TCP/IP協議、socket編程、TCP網路編程、UDP網路編程、Web編程開發等方面入手,全面了解Linux網路應用程序開發。重點學習網路編程相關API,熟練掌握TCP協議伺服器的編程方法和並發伺服器的實現,了解HTTP協議及其實現方法,熟悉UDP廣播、多播的原理及編程方法,掌握混合C/S架構網路通信系統的設計,熟悉HTML,Javascript等Web編程技術及實現方法。
五:數據結構與演算法 數據結構及演算法在嵌入式底層驅動、通信協議、及各種引擎開發中會得到大量應用,對其掌握的好壞直接影響程序的效率、簡潔及健壯性。此階段的學習要重點理解數據結構與演算法的基礎內容,包括順序表、鏈表、隊列、棧、樹、圖、哈希表、各種查找排序演算法等應用及其C語言實現過程。
六:C++ 、QT C++是Linux應用開發主要語言之一,本階段重點掌握面向對象編程的基本思想以及C++的重要內容。圖形界面編程是嵌入式開發中非常重要的一個環節。由於QT具有跨平台、面向對象、豐富API、支持2D/3D渲染、支持XML、多國語等強大功能,在嵌入式領域的GUI開發中得到了廣范的應用,在本階段通過基於QT圖形庫的學習使學員可以熟練編寫GUI程序,並移植QT應用程序到Cortex-A8平台。包括IDE使用、QT部件及布局管理器、信息與槽機制的應用、滑鼠、鍵盤及繪圖事件處理及文件處理的應用。
七:Cortex A8 、Linux 平台開發 通過基於ARM Cortex-A8處理s5pv210了解晶元手冊的基本閱讀技巧,掌握s5pv210系統資源、時鍾控制器、電源管理、異常中斷控制器、nand flash控制器等模塊,為底層平台搭建做好准備。Linux平台包括內核裁減、內核移植、交叉編譯、GNU工具使用、內核調試、Bootloader介紹、製作與原理分析、根文件系統製作以及向內核中添加自己的模塊,並在s5pv210實驗平台上運行自己製作的Linux系統,集成部署Linux系統整個流程。同時了解Android操作系統開發流程。Android系統是基於Linux平台的開源操作系統,該平台由操作系統、中間件、用戶界面和應用軟體組成,是首個為移動終端打造的真正開放和完整的移動軟體,目前它的應用不再局限於移動終端,還包括數據電視、機頂盒、PDA等消費類電子產品。
八:驅動開發 驅動程序設計是嵌入式Linux開發工作中重要的一部分,也是比較困難的一部分。本階段的學習要熟悉Linux的內核機制、驅動程序與用戶級應用程序的介面,掌握系統對設備的並發操作。熟悉所開發硬體的工作原理,具備ARM硬體介面的基礎知識,熟悉ARM Cortex-A8處理器s5pv210各資源、掌握Linux設備驅動原理框架,熟悉工程中常見Linux高級字元設備、塊設備、網路設備、USB設備等驅動開發,在工作中能獨立勝任底層驅動開發。
以上就是列出的關於一名合格嵌入式Linux開發工程師所必學的理論知識,其實,作為一個嵌入式開發人員,專業知識和項目經驗同樣重要,所以在我們的理論學習中也要有一定的項目實踐,鍛煉自己的項目開發能力。

6. 一個Linux多進程編程

1 引言
對於沒有接觸過Unix/Linux操作系統的人來說,fork是最難理解的概念之一:它執行一次卻返回兩個值。fork函數是Unix系統最傑出的成就之一,它是七十年代UNIX早期的開發者經過長期在理論和實踐上的艱苦探索後取得的成果,一方面,它使操作系統在進程管理上付出了最小的代價,另一方面,又為程序員提供了一個簡潔明了的多進程方法。與DOS和早期的Windows不同,Unix/Linux系統是真正實現多任務操作的系統,可以說,不使用多進程編程,就不能算是真正的Linux環境下編程。
多線程程序設計的概念早在六十年代就被提出,但直到八十年代中期,Unix系統中才引入多線程機制,如今,由於自身的許多優點,多線程編程已經得到了廣泛的應用。
下面,我們將介紹在Linux下編寫多進程和多線程程序的一些初步知識。

2 多進程編程
什麼是一個進程?進程這個概念是針對系統而不是針對用戶的,對用戶來說,他面對的概念是程序。當用戶敲入命令執行一個程序的時候,對系統而言,它將啟動一個進程。但和程序不同的是,在這個進程中,系統可能需要再啟動一個或多個進程來完成獨立的多個任務。多進程編程的主要內容包括進程式控制制和進程間通信,在了解這些之前,我們先要簡單知道進程的結構。

2.1 Linux下進程的結構
Linux下一個進程在內存里有三部分的數據,就是"代碼段"、"堆棧段"和"數據段"。其實學過匯編語言的人一定知道,一般的CPU都有上述三種段寄存器,以方便操作系統的運行。這三個部分也是構成一個完整的執行序列的必要的部分。
"代碼段",顧名思義,就是存放了程序代碼的數據,假如機器中有數個進程運行相同的一個程序,那麼它們就可以使用相同的代碼段。"堆棧段"存放的就是子程序的返回地址、子程序的參數以及程序的局部變數。而數據段則存放程序的全局變數,常數以及動態數據分配的數據空間(比如用malloc之類的函數取得的空間)。這其中有許多細節問題,這里限於篇幅就不多介紹了。系統如果同時運行數個相同的程序,它們之間就不能使用同一個堆棧段和數據段。

2.2 Linux下的進程式控制制
在傳統的Unix環境下,有兩個基本的操作用於創建和修改進程:函數fork( )用來創建一個新的進程,該進程幾乎是當前進程的一個完全拷貝;函數族exec( )用來啟動另外的進程以取代當前運行的進程。Linux的進程式控制制和傳統的Unix進程式控制制基本一致,只在一些細節的地方有些區別,例如在Linux系統中調用vfork和fork完全相同,而在有些版本的Unix系統中,vfork調用有不同的功能。由於這些差別幾乎不影響我們大多數的編程,在這里我們不予考慮。
2.2.1 fork( )
fork在英文中是"分叉"的意思。為什麼取這個名字呢?因為一個進程在運行中,如果使用了fork,就產生了另一個進程,於是進程就"分叉"了,所以這個名字取得很形象。下面就看看如何具體使用fork,這段程序演示了使用fork的基本框架:

void main(){
int i;
if ( fork() == 0 ) {
/* 子進程程序 */
for ( i = 1; i <1000; i ++ ) printf("This is child process\n");
}
else {
/* 父進程程序*/
for ( i = 1; i <1000; i ++ ) printf("This is process process\n");
}
}
程序運行後,你就能看到屏幕上交替出現子進程與父進程各列印出的一千條信息了。如果程序還在運行中,你用ps命令就能看到系統中有兩個它在運行了。
那麼調用這個fork函數時發生了什麼呢?fork函數啟動一個新的進程,前面我們說過,這個進程幾乎是當前進程的一個拷貝:子進程和父進程使用相同的代碼段;子進程復制父進程的堆棧段和數據段。這樣,父進程的所有數據都可以留給子進程,但是,子進程一旦開始運行,雖然它繼承了父進程的一切數據,但實際上數據卻已經分開,相互之間不再有影響了,也就是說,它們之間不再共享任何數據了。它們再要交互信息時,只有通過進程間通信來實現,這將是我們下面的內容。既然它們如此相象,系統如何來區分它們呢?這是由函數的返回值來決定的。對於父進程,fork函數返回了子程序的進程號,而對於子程序,fork函數則返回零。在操作系統中,我們用ps函數就可以看到不同的進程號,對父進程而言,它的進程號是由比它更低層的系統調用賦予的,而對於子進程而言,它的進程號即是fork函數對父進程的返回值。在程序設計中,父進程和子進程都要調用函數fork()下面的代碼,而我們就是利用fork()函數對父子進程的不同返回值用if...else...語句來實現讓父子進程完成不同的功能,正如我們上面舉的例子一樣。我們看到,上面例子執行時兩條信息是交互無規則的列印出來的,這是父子進程獨立執行的結果,雖然我們的代碼似乎和串列的代碼沒有什麼區別。
讀者也許會問,如果一個大程序在運行中,它的數據段和堆棧都很大,一次fork就要復制一次,那麼fork的系統開銷不是很大嗎?其實UNIX自有其解決的辦法,大家知道,一般CPU都是以"頁"為單位來分配內存空間的,每一個頁都是實際物理內存的一個映像,象INTEL的CPU,其一頁在通常情況下是4086位元組大小,而無論是數據段還是堆棧段都是由許多"頁"構成的,fork函數復制這兩個段,只是"邏輯"上的,並非"物理"上的,也就是說,實際執行fork時,物理空間上兩個進程的數據段和堆棧段都還是共享著的,當有一個進程寫了某個數據時,這時兩個進程之間的數據才有了區別,系統就將有區別的"頁"從物理上也分開。系統在空間上的開銷就可以達到最小。
下面演示一個足以"搞死"Linux的小程序,其源代碼非常簡單:
void main()
{
for( ; ; ) fork();
}
這個程序什麼也不做,就是死循環地fork,其結果是程序不斷產生進程,而這些進程又不斷產生新的進程,很快,系統的進程就滿了,系統就被這么多不斷產生的進程"撐死了"。當然只要系統管理員預先給每個用戶設置可運行的最大進程數,這個惡意的程序就完成不了企圖了。
2.2.2 exec( )函數族
下面我們來看看一個進程如何來啟動另一個程序的執行。在Linux中要使用exec函數族。系統調用execve()對當前進程進行替換,替換者為一個指定的程序,其參數包括文件名(filename)、參數列表(argv)以及環境變數(envp)。exec函數族當然不止一個,但它們大致相同,在Linux中,它們分別是:execl,execlp,execle,execv,execve和execvp,下面我只以execlp為例,其它函數究竟與execlp有何區別,請通過manexec命令來了解它們的具體情況。
一個進程一旦調用exec類函數,它本身就"死亡"了,系統把代碼段替換成新的程序的代碼,廢棄原有的數據段和堆棧段,並為新程序分配新的數據段與堆棧段,唯一留下的,就是進程號,也就是說,對系統而言,還是同一個進程,不過已經是另一個程序了。(不過exec類函數中有的還允許繼承環境變數之類的信息。)
那麼如果我的程序想啟動另一程序的執行但自己仍想繼續運行的話,怎麼辦呢?那就是結合fork與exec的使用。下面一段代碼顯示如何啟動運行其它程序:

char command[256];
void main()
{
int rtn; /*子進程的返回數值*/
while(1) {
/* 從終端讀取要執行的命令 */
printf( ">" );
fgets( command, 256, stdin );
command[strlen(command)-1] = 0;
if ( fork() == 0 ) {
/* 子進程執行此命令 */
execlp( command, command );
/* 如果exec函數返回,表明沒有正常執行命令,列印錯誤信息*/
perror( command );
exit( errorno );
}
else {
/* 父進程, 等待子進程結束,並列印子進程的返回值 */
wait ( &rtn );
printf( " child process return %d\n",. rtn );
}
}
}

此程序從終端讀入命令並執行之,執行完成後,父進程繼續等待從終端讀入命令。熟悉DOS和WINDOWS系統調用的朋友一定知道DOS/WINDOWS也有exec類函數,其使用方法是類似的,但DOS/WINDOWS還有spawn類函數,因為DOS是單任務的系統,它只能將"父進程"駐留在機器內再執行"子進程",這就是spawn類的函數。WIN32已經是多任務的系統了,但還保留了spawn類函數,WIN32中實現spawn函數的方法同前述UNIX中的方法差不多,開設子進程後父進程等待子進程結束後才繼續運行。UNIX在其一開始就是多任務的系統,所以從核心角度上講不需要spawn類函數。
在這一節里,我們還要講講system()和popen()函數。system()函數先調用fork(),然後再調用exec()來執行用戶的登錄shell,通過它來查找可執行文件的命令並分析參數,最後它么使用wait()函數族之一來等待子進程的結束。函數popen()和函數system()相似,不同的是它調用pipe()函數創建一個管道,通過它來完成程序的標准輸入和標准輸出。這兩個函數是為那些不太勤快的程序員設計的,在效率和安全方面都有相當的缺陷,在可能的情況下,應該盡量避免。

2.3 Linux下的進程間通信
詳細的講述進程間通信在這里絕對是不可能的事情,而且筆者很難有信心說自己對這一部分內容的認識達到了什麼樣的地步,所以在這一節的開頭首先向大家推薦著名作者Richard Stevens的著名作品:《Advanced Programming in the UNIX Environment》,它的中文譯本《UNIX環境高級編程》已有機械工業出版社出版,原文精彩,譯文同樣地道,如果你的確對在Linux下編程有濃厚的興趣,那麼趕緊將這本書擺到你的書桌上或計算機旁邊來。說這么多實在是難抑心中的景仰之情,言歸正傳,在這一節里,我們將介紹進程間通信最最初步和最最簡單的一些知識和概念。
首先,進程間通信至少可以通過傳送打開文件來實現,不同的進程通過一個或多個文件來傳遞信息,事實上,在很多應用系統里,都使用了這種方法。但一般說來,進程間通信(IPC:InterProcess Communication)不包括這種似乎比較低級的通信方法。Unix系統中實現進程間通信的方法很多,而且不幸的是,極少方法能在所有的Unix系統中進行移植(唯一一種是半雙工的管道,這也是最原始的一種通信方式)。而Linux作為一種新興的操作系統,幾乎支持所有的Unix下常用的進程間通信方法:管道、消息隊列、共享內存、信號量、套介面等等。下面我們將逐一介紹。
2.3.1 管道
管道是進程間通信中最古老的方式,它包括無名管道和有名管道兩種,前者用於父進程和子進程間的通信,後者用於運行於同一台機器上的任意兩個進程間的通信。
無名管道由pipe()函數創建:
#include <unistd.h>
int pipe(int filedis[2]);
參數filedis返回兩個文件描述符:filedes[0]為讀而打開,filedes[1]為寫而打開。filedes[1]的輸出是filedes[0]的輸入。下面的例子示範了如何在父進程和子進程間實現通信。

#define INPUT 0
#define OUTPUT 1

void main() {
int file_descriptors[2];
/*定義子進程號 */
pid_t pid;
char buf[256];
int returned_count;
/*創建無名管道*/
pipe(file_descriptors);
/*創建子進程*/
if((pid = fork()) == -1) {
printf("Error in fork\n");
exit(1);
}
/*執行子進程*/
if(pid == 0) {
printf("in the spawned (child) process...\n");
/*子進程向父進程寫數據,關閉管道的讀端*/
close(file_descriptors[INPUT]);
write(file_descriptors[OUTPUT], "test data", strlen("test data"));
exit(0);
} else {
/*執行父進程*/
printf("in the spawning (parent) process...\n");
/*父進程從管道讀取子進程寫的數據,關閉管道的寫端*/
close(file_descriptors[OUTPUT]);
returned_count = read(file_descriptors[INPUT], buf, sizeof(buf));
printf("%d bytes of data received from spawned process: %s\n",
returned_count, buf);
}
}
在Linux系統下,有名管道可由兩種方式創建:命令行方式mknod系統調用和函數mkfifo。下面的兩種途徑都在當前目錄下生成了一個名為myfifo的有名管道:
方式一:mkfifo("myfifo","rw");
方式二:mknod myfifo p
生成了有名管道後,就可以使用一般的文件I/O函數如open、close、read、write等來對它進行操作。下面即是一個簡單的例子,假設我們已經創建了一個名為myfifo的有名管道。
/* 進程一:讀有名管道*/
#include <stdio.h>
#include <unistd.h>
void main() {
FILE * in_file;
int count = 1;
char buf[80];
in_file = fopen("mypipe", "r");
if (in_file == NULL) {
printf("Error in fdopen.\n");
exit(1);
}
while ((count = fread(buf, 1, 80, in_file)) > 0)
printf("received from pipe: %s\n", buf);
fclose(in_file);
}
/* 進程二:寫有名管道*/
#include <stdio.h>
#include <unistd.h>
void main() {
FILE * out_file;
int count = 1;
char buf[80];
out_file = fopen("mypipe", "w");
if (out_file == NULL) {
printf("Error opening pipe.");
exit(1);
}
sprintf(buf,"this is test data for the named pipe example\n");
fwrite(buf, 1, 80, out_file);
fclose(out_file);
}

2.3.2 消息隊列
消息隊列用於運行於同一台機器上的進程間通信,它和管道很相似,事實上,它是一種正逐漸被淘汰的通信方式,我們可以用流管道或者套介面的方式來取代它,所以,我們對此方式也不再解釋,也建議讀者忽略這種方式。

2.3.3 共享內存
共享內存是運行在同一台機器上的進程間通信最快的方式,因為數據不需要在不同的進程間復制。通常由一個進程創建一塊共享內存區,其餘進程對這塊內存區進行讀寫。得到共享內存有兩種方式:映射/dev/mem設備和內存映像文件。前一種方式不給系統帶來額外的開銷,但在現實中並不常用,因為它控制存取的將是實際的物理內存,在Linux系統下,這只有通過限制Linux系統存取的內存才可以做到,這當然不太實際。常用的方式是通過shmXXX函數族來實現利用共享內存進行存儲的。
首先要用的函數是shmget,它獲得一個共享存儲標識符。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, int size, int flag);
這個函數有點類似大家熟悉的malloc函數,系統按照請求分配size大小的內存用作共享內存。Linux系統內核中每個IPC結構都有的一個非負整數的標識符,這樣對一個消息隊列發送消息時只要引用標識符就可以了。這個標識符是內核由IPC結構的關鍵字得到的,這個關鍵字,就是上面第一個函數的key。數據類型key_t是在頭文件sys/types.h中定義的,它是一個長整形的數據。在我們後面的章節中,還會碰到這個關鍵字。
當共享內存創建後,其餘進程可以調用shmat()將其連接到自身的地址空間中。
void *shmat(int shmid, void *addr, int flag);
shmid為shmget函數返回的共享存儲標識符,addr和flag參數決定了以什麼方式來確定連接的地址,函數的返回值即是該進程數據段所連接的實際地址,進程可以對此進程進行讀寫操作。
使用共享存儲來實現進程間通信的注意點是對數據存取的同步,必須確保當一個進程去讀取數據時,它所想要的數據已經寫好了。通常,信號量被要來實現對共享存儲數據存取的同步,另外,可以通過使用shmctl函數設置共享存儲內存的某些標志位如SHM_LOCK、SHM_UNLOCK等來實現。

7. 淺談linux 多線程編程和 windows 多線程編程的異同

首先我們講講為什麼要採用多線程編程,其實並不是所有的程序都必須採用多線程,有些時候採用多線程,性能還沒有單線程好。所以我們要搞清楚,什麼時候採用多線程。採用多線程的好處如下:
(1)因為多線程彼此之間採用相同的地址空間,共享大部分的數據,這樣和多進程相比,代價比較節儉,因為多進程的話,啟動新的進程必須分配給它獨立的地址空間,這樣需要數據表來維護代碼段,數據段和堆棧段等等。
(2)多線程和多進程相比,一個明顯的優點就是線程之間的通信了,對不同進程來說,它們具有獨立的數據空間,要進行數據的傳遞只能通過通信的方式進行,這種方式不僅費時,而且很不方便。但是對於多線程就不一樣了。他們之間可以直接共享數據,比如最簡單的方式就是共享全局變數。但是共享全部變數也要注意哦,呵呵,必須注意同步,不然後果你知道的。呵呵。
(3)在多cpu的情況下,不同的線程可以運行不同的cpu下,這樣就完全並行了。
反正我覺得在這種情況下,採用多線程比較理想。比如說你要做一個任務分2個步驟,你為提高工作效率,你可以多線程技術,開辟2個線程,第一個線程就做第一步的工作,第2個線程就做第2步的工作。但是你這個時候要注意同步了。因為只有第一步做完才能做第2步的工作。這時,我們可以採用同步技術進行線程之間的通信。
針對這種情況,我們首先講講多線程之間的通信,在windows平台下,多線程之間通信採用的方法主要有:
(1)共享全局變數,這種方法是最容易想到的,呵呵,那就首先講講吧,比如說吧,上面的問題,第一步要向第2步傳遞收據,我們可以之間共享全局變數,讓兩個線程之間傳遞數據,這時主要考慮的就是同步了,因為你後面的線程在對數據進行操作的時候,你第一個線程又改變了數據的內容,你不同步保護,後果很嚴重的。你也知道,這種情況就是讀臟數據了。在這種情況下,我們最容易想到的同步方法就是設置一個bool flag了,比如說在第2個線程還沒有用完數據前,第一個線程不能寫入。有時在2個線程所需的時間不相同的時候,怎樣達到最大效率的同步,就比較麻煩了。咱們可以多開幾個緩沖區進行操作。就像生產者消費者一樣了。如果是2個線程一直在跑的,由於時間不一致,緩沖區遲早會溢出的。在這種情況下就要考慮了,是不讓數據寫入還是讓數據覆蓋掉老的數據,這時候就要具體問題具體分析了。就此打住,呵呵。就是用bool變數控制同步,linux 和windows是一樣的。
既然講道了這里,就再講講其它同步的方法。同樣 針對上面的這個問題,共享全局變數同步問題。除了採用bool變數外,最容易想到的方法就是互斥量了。呵呵,也就是傳說中的加鎖了。windows下加鎖和linux下加鎖是類似的。採用互斥量進行同步,要想進入那段代碼,就先必須獲得互斥量。
linux上互斥量的函數是:
windows下互斥量的函數有:createmutex 創建一個互斥量,然後就是獲得互斥量waitforsingleobject函數,用完了就釋放互斥量ReleaseMutex(hMutex),當減到0的時候 內核會才會釋放其對象。下面是windows下與互斥的幾個函數原型。
HANDLE WINAPI CreateMutex(
__in LPSECURITY_ATTRIBUTES lpMutexAttributes,
__in BOOL bInitialOwner,
__in LPCTSTR lpName
);
可以可用來創建一個有名或無名的互斥量對象
第一參數 可以指向一個結構體SECURITY_ATTRIBUTES一般可以設為null;
第二參數 指當時的函數是不是感應感應狀態 FALSE為當前擁有者不會創建互斥
第三參數 指明是否是有名的互斥對象 如果是無名 用null就好。
DWORD WINAPI WaitForSingleObject(
__in HANDLE hHandle,
__in DWORD dwMilliseconds
);
第一個是 創建的互斥對象的句柄。第二個是 表示將在多少時間之後返回 如果設為宏INFINITE 則不會返回 直到用戶自己定義返回。
對於linux操作系統,互斥也是類似的,只是函數不同罷了。在linux下,和互斥相關的幾個函數也要閃亮登場了。
pthread_mutex_init函數:初始化一個互斥鎖;
pthread_mutex_destroy函數:注銷一個互斥鎖;
pthread_mutex_lock函數:加鎖,如果不成功,阻塞等待;
pthread_mutex_unlock函數:解鎖;
pthread_mutex_trylock函數:測試加鎖,如果不成功就立即返回,錯誤碼為EBUSY;
至於這些函數的用法,google上一搜,就出來了,呵呵,在這里不多講了。windows下還有一個可以用來保護數據的方法,也是線程同步的方式
就是臨界區了。臨界區和互斥類似。它們之間的區別是,臨界區速度快,但是它只能用來同步同一個進程內的多個線程。臨界區的獲取和釋放函數如下:
EnterCriticalSection() 進入臨界區; LeaveCriticalSection()離開臨界區。 對於多線程共享內存的東東就講到這里了。
(2)採用消息機制進行多線程通信和同步,windows下面的的消息機制的函數用的多的就是postmessage了。Linux下的消息機制,我用的較少,就不在這里說了,如果誰熟悉的,也告訴我,呵呵。
(3)windows下的另外一種線程通信方法就是事件和信號量了。同樣針對我開始舉得例子,2個線程同步,他們之間傳遞信息,可以採用事件(Event)或信號量(Semaphore),比如第一個線程完成生產的數據後,就必須告訴第2個線程,他已經把數據准備好了,你可以來取走了。第2個線程就把數據取走。呵呵,這里可以採用消息機制,當第一個線程准備好數據後,就直接postmessage給第2個線程,按理說採用postmessage一個線程就可以搞定這個問題了。呵呵,不是重點,省略不講了。
對於linux,也有類似的方法,就是條件變數了,呵呵,這里windows和linux就有不同了。要特別講講才行。
對於windows,採用事件和信號量同步時候,都會使用waitforsingleobject進行等待的,這個函數的第一個參數是一個句柄,在這里可以是Event句柄,或Semaphore句柄,第2個參數就是等待的延遲,最終等多久,單位是ms,如果這個參數為INFINITE,那麼就是無限等待了。釋放信號量的函數為ReleaseSemaphore();釋放事件的函數為SetEvent。當然使用這些東西都要初始化的。這里就不講了。Msdn一搜,神馬都出來了,呵呵。神馬都是浮雲!
對於linux操作系統,是採用條件變數來實現類似的功能的。Linux的條件變數一般都是和互斥鎖一起使用的,主要的函數有:
pthread_mutex_lock ,
pthread_mutex_unlock,
pthread_cond_init
pthread_cond_signal
pthread_cond_wait
pthread_cond_timewait
為了和windows操作系統進行對比,我用以下表格進行比較:

對照以上表格,總結如下:
(1) Pthread_cleanup_push,Pthread_cleanup_pop:
這一對函數push和pop的作用是當出現異常退出時,做一些清除操作,即當在push和pop函數之間異常退出,包括調用pthread_exit退出,都會執行push裡面的清除函數,如果有多個push,注意是是棧,先執行後面的那個函數,在執行前面的函數,但是注意當在這2個函數之間通過return 退出的話,執不執行push後的函數就看pop函數中的參數是不是為0了。還有當沒有異常退出時,等同於在這裡面return退出的情況,即:當pop函數參數不為0時,執行清除操作,當pop函數參數為0時,不執行push函數中的清除函數。
(2)linux的pthread_cond_signal和SetEvent的不同點
Pthread_cond_singal釋放信號後,當沒有Pthread_cond_wait,信號馬上復位了,這點和SetEvent不同,SetEvent是不會復位的。詳解如下:
條件變數的置位和復位有2種常用模型:第一種模型是當條件變數置位時(signaled)以後,如果當前沒有線程在等待,其狀態會保持為置位(signaled),直到有等待的線程進入被觸發,其狀態才會變為unsignaled,這種模型以採用Windows平台上的Auto-set Event 為代表。
第2種模型則是Linux平台的pthread所採用的模型,當條件變數置位(signaled)以後,即使當前沒有任何線程在等待,其狀態也會恢復為復位(unsignaled)狀態。
條件變數在Linux平台上的這種模型很難說好壞,在實際應用中,我們可以對
代碼稍加改進就可以避免這種差異的發生。由於這種差異只會發生在觸發沒有被線程等待在條件變數的時刻,因此我們只需要掌握好觸發的時機即可。最簡單的做法是增加一個計數器記錄等待線程的個數,在決定觸發條件變數前檢查該變數即可。
示例 使用 pthread_cond_wait() 和 pthread_cond_signal()
pthread_mutex_t count_lock;
pthread_cond_t count_nonzero;
unsigned count;
decrement_count()
{
pthread_mutex_lock(&count_lock);
while (count == 0)
pthread_cond_wait(&count_nonzero, &count_lock);
count = count - 1;
pthread_mutex_unlock(&count_lock);
}
increment_count()
{
pthread_mutex_lock(&count_lock);
if (count == 0)
pthread_cond_signal(&count_nonzero);
count = count + 1;
pthread_mutex_unlock(&count_lock);
}
(3) 注意Pthread_cond_wait條件返回時互斥鎖的解鎖問題
extern int pthread_cond_wait __P ((pthread_cond_t *__cond,pthread_mutex_t *__mutex));
調用這個函數時,線程解開mutex指向的鎖並被條件變數cond阻塞。線程可以被函數pthread_cond_signal和函數 pthread_cond_broadcast喚醒線程被喚醒後,它將重新檢查判斷條件是否滿足,如果還不滿足,一般說來線程應該仍阻塞在這里,被等待被下一次喚醒。如果在多線程中採用pthread_cond_wait來等待時,會首先釋放互斥鎖,當等待的信號到來時,再次獲得互斥鎖,因此在之後要注意手動解鎖。舉例如下:
#include
#include
#include
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; /*初始化互斥鎖*/
pthread_cond_t cond = PTHREAD_COND_INITIALIZER; //初始化條件變數
void *thread1(void *);
void *thread2(void *);
int i=1;
int main(void)
{
pthread_t t_a;
pthread_t t_b;
pthread_create(&t_a,NULL,thread1,(void *)NULL);/*創建進程t_a*/
pthread_create(&t_b,NULL,thread2,(void *)NULL); /*創建進程t_b*/
pthread_join(t_b, NULL);/*等待進程t_b結束*/
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
exit(0);
}
void *thread1(void *junk)
{
for(i=1;i<=9;i++)
{
printf("IN one\n");
pthread_mutex_lock(&mutex);//
if(i%3==0)
pthread_cond_signal(&cond);/*,發送信號,通知t_b進程*/
else
printf("thead1:%d\n",i);
pthread_mutex_unlock(&mutex);//*解鎖互斥量*/
printf("Up Mutex\n");
sleep(3);
}
}
void *thread2(void *junk)
{
while(i<9)
{
printf("IN two \n");
pthread_mutex_lock(&mutex);
if(i%3!=0)
pthread_cond_wait(&cond,&mutex);/*等待*/
printf("thread2:%d\n",i);
pthread_mutex_unlock(&mutex);
printf("Down Mutex\n");
sleep(3);
}
}
輸出如下:
IN one
thead1:1
Up Mutex
IN two
IN one
thead1:2
Up Mutex
IN one
thread2:3
Down Mutex
Up Mutex
IN one
thead1:4
Up Mutex
IN two
IN one
thead1:5
Up Mutex
IN one
Up Mutex
thread2:6
Down Mutex
IN two
thread2:6
Down Mutex
IN one
thead1:7
Up Mutex
IN one
thead1:8
Up Mutex
IN two
IN one
Up Mutex
thread2:9
Down Mutex
注意藍色的地方,有2個thread2:6,其實當這個程序多執行幾次,i=3和i=6時有可能多列印幾個,這里就是競爭鎖造成的了。
(4)另外要注意的Pthread_cond_timedwait等待的是絕對時間,這個和WaitForSingleObject是不同的,Pthread_cond_timedwait在網上也有討論。如下:這個問題比較經典,我把它搬過來。
thread_a :
pthread_mutex_lock(&mutex);
//do something
pthread_mutex_unlock(&mutex)
thread_b:
pthread_mutex_lock(&mutex);
//do something
pthread_cond_timedwait(&cond, &mutex, &tm);
pthread_mutex_unlock(&mutex)
有如上兩個線程thread_a, thread_b,現在如果a已經進入了臨界區,而b同時超時了,那麼b會從pthread_cond_timedwait返回嗎?如果能返回,那豈不是a,b都在臨界區?如果不能返回,那pthread_cond_timedwait的定時豈不是就不準了?
大家討論有價值的2點如下:
(1) pthread_cond_timedwait (pthread_cond_t *cv, pthread_mutex_t *external_mutex, const struct timespec *abstime) -- This function is a time-based variant of pthread_cond_wait. It waits up to abstime amount of time for cv to be notified. If abstime elapses before cv is notified, the function returns back to the caller with an ETIME result, signifying that a timeout has occurred. Even in the case of timeouts, the external_mutex will be locked when pthread_cond_timedwait returns.
(2) 2.1 pthread_cond_timedwait行為和pthread_cond_wait一樣,在返回的時候都要再次lock mutex.
2 .2pthread_cond_timedwait所謂的如果沒有等到條件變數,超時就返回,並不確切。
如果pthread_cond_timedwait超時到了,但是這個時候不能lock臨界區,pthread_cond_timedwait並不會立即返回,但是在pthread_cond_timedwait返回的時候,它仍在臨界區中,且此時返回值為ETIMEDOUT。
關於pthread_cond_timedwait超時返回的問題,我也認同觀點2。
附錄:
int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict_attr,void*(*start_rtn)(void*),void *restrict arg);
返回值:若成功則返回0,否則返回出錯編號
返回成功時,由tidp指向的內存單元被設置為新創建線程的線程ID。attr參數用於制定各種不同的線程屬性。新創建的線程從start_rtn函數的地址開始運行,該函數只有一個無指針參數arg,如果需要向start_rtn函數傳遞的參數不止一個,那麼需要把這些參數放到一個結構中,然後把這個結構的地址作為arg的參數傳入。
linux下用C開發多線程程序,Linux系統下的多線程遵循POSIX線程介面,稱為pthread。
由 restrict 修飾的指針是最初唯一對指針所指向的對象進行存取的方法,僅當第二個指針基於第一個時,才能對對象進行存取。對對象的存取都限定於基於由 restrict 修飾的指針表達式中。 由 restrict 修飾的指針主要用於函數形參,或指向由 malloc() 分配的內存空間。restrict 數據類型不改變程序的語義。 編譯器能通過作出 restrict 修飾的指針是存取對象的唯一方法的假設,更好地優化某些類型的常式。
第一個參數為指向線程標識符的指針。
第二個參數用來設置線程屬性。
第三個參數是線程運行函數的起始地址。
第四個參數是運行函數的參數。
因為pthread不是linux系統的庫,所以在編譯時注意加上-lpthread參數,以調用靜態鏈接庫。
終止線程:
如果在進程中任何一個線程中調用exit或_exit,那麼整個進行會終止,線程正常的退出方式有:
(1) 線程從啟動常式中返回(return)
(2) 線程可以被另一個進程終止(kill);
(3) 線程自己調用pthread_exit函數
#include
pthread_exit
線程等待:
int pthread_join(pthread_t tid,void **rval_ptr)
函數pthread_join用來等待一個線程的結束。函數原型為:
extern int pthread_join __P (pthread_t __th, void **__thread_return);
第一個參數為被等待的線程標識符,第二個參數為一個用戶定義的指針,它可以用來存儲被等待線程的返回值。這個函數是一個線程阻塞的函數,調用它的函數將一直等待到被等待的線程結束為止,當函數返回時,被等待線程的資源被收回。
對於windows線程的創建東西,就不列舉了,msdn上 一搜就出來了。呵呵。今天就講到這里吧,希望是拋磚引玉,大家一起探討,呵呵。部分內容我也是參考internet的,特此對原作者表示感謝!

8. Linux多線程編程

程序代碼test.c共兩個線程,一個主線程,一個讀緩存區的線程:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
char globe_buffer[100];

void *read_buffer_thread(void *arg); //這里先聲明一下讀緩存的線程,具體實現寫在後面了

int main()
{
int res,i;
pthread_t read_thread;
for(i=0;i<20;i++)
globe_buffer[i]=i;
printf("\nTest thread : write buffer finish\n");
sleep(3);\\這里的3秒是多餘,可以不要。
res = pthread_create(&read_thread, NULL, read_buffer_thread, NULL);
if (res != 0)
{
printf("Read Thread creat Error!");
exit(0);
}
sleep(1);
printf("waiting for read thread to finish...\n");

res = pthread_join(read_thread, NULL);
if (res != 0)
{
printf("read thread join failed!\n");
exit(0);
}
printf("read thread test OK, have fun!! exit ByeBye\n");
return 0;
}
void *read_buffer_thread(void *arg)
{
int i,x;
printf("Read buffer thread read data : \n");
for(i=0;i<20;i++)
{
x=globe_buffer[i];
printf("%d ",x);
globe_buffer[i]=0;//清空
}
printf("\nread over\n");
}
---------------------------------------------------------------------------------
以上程序編譯:
gcc -D_REENTRANT test.c -o test.o –lpthread
運行這個程序:
$ ./test.o:

9. 在Linux系統上如何進行openmp多線程編程

多線程的使用主要是用來處理程序「在一部分上會阻塞」,「在另一部分上需要持續運行」的場合。一般是根據需求,可以用多線程,事件觸發,callback等方法達到。但是有一些方法是只有多線程能辦到的就只有用多線程或者多進程來完成。

10. Linux下如何實現shell多線程編程以提高應用程序的響應

Linux中多線程編程擁有提高應用程序的響應、使多cpu系統更加有效等優點,下面小編將通過Linux下shell多線程編程的例子給大家講解下多線程編程的過程,一起來了解下吧。

#!/bin/bash

#———————————————————————————–

# 此例子說明了一種用wait、read命令模擬多線程的一種技巧

# 此技巧往往用於多主機檢查,比如ssh登錄、ping等等這種單進程比較慢而不耗費cpu的情況

# 還說明了多線程的控制

#———————————————————————————–

function a_sub

{

# 此處定義一個函數,作為一個線程(子進程)

sleep 3 # 線程的作用是sleep 3s

}

tmp_fifofile=「/tmp/$.fifo」 mkfifo $tmp_fifofile # 新建一個fifo類型的文件

exec 6《》$tmp_fifofile # 將fd6指向fifo類型

rm $tmp_fifofile thread=15 # 此處定義線程數

for

((i=0;i《$thread;i++));do echo

done 》&6 # 事實上就是在fd6中放置了$thread個回車符

for

((i=0;i《50;i++));do # 50次循環,可以理解為50個主機,或其他

read -u6 # 一個read -u6命令執行一次,就從fd6中減去一個回車符,然後向下執行,

# fd6中沒有回車符的時候,就停在這了,從而實現了線程數量控制

{ # 此處子進程開始執行,被放到後台

a_sub &&

{ # 此處可以用來判斷子進程的邏輯

echo 「a_sub is finished」

}

||

{ echo 「sub error」

}

echo 》&6 # 當進程結束以後,再向fd6中加上一個回車符,即補上了read -u6減去的那個

}

& done wait # 等待所有的後檯子進程結束

exec 6》&- # 關閉df6 exit 0

說明:

此程序中的命令

mkfifo tmpfile

和linux中的命令

mknod tmpfile p

效?果相同。區別是mkfifo為POSIX標准,因此推薦使用它。該命令創建了一個先入先出的管道文件,並為其分配文件標志符6。管道文件是進程之間通信的一種方式,注意這一句很重要

exec 6《》$tmp_fifofile # 將fd6指向fifo類型

如果沒有這句,在向文件$tmp_fifofile或者&6寫入數據時,程序會被阻塞,直到有read讀出了管道文件中的數據為止。而執行了上面這一句後就可以在程序運行期間不斷向fifo類型的文件寫入數據而不會阻塞,並且數據會被保存下來以供read程序讀出。

通過運行命令:

time 。/multithread.sh 》/dev/null

最終運算時間: 50/15 = 3組(每組15)+1組(5個《15 組成一個組)= 4組,每組花費時間:3秒,

則 3 * 4 = 12 秒。

傳統非多線程的代碼 運算時間: 50 * 3 = 150 秒。

上面就是Linux下shell多線程編程的實例介紹了,使用多線程編程還能夠改善程序結構,有興趣的朋友不妨試試看吧。

熱點內容
wemall微商城源碼 發布:2025-05-14 22:15:20 瀏覽:802
隆地優選交易密碼是什麼 發布:2025-05-14 21:53:23 瀏覽:93
強酸強鹼存儲櫃 發布:2025-05-14 21:45:16 瀏覽:563
車輛參數配置包括什麼 發布:2025-05-14 21:31:03 瀏覽:163
怎麼引入安卓項目 發布:2025-05-14 21:26:39 瀏覽:824
游戲輔編程 發布:2025-05-14 21:18:49 瀏覽:687
三菱plc一段二段密碼什麼意思 發布:2025-05-14 21:17:16 瀏覽:528
電腦開機密碼忘記了怎麼破解 發布:2025-05-14 21:09:40 瀏覽:57
pythondict格式 發布:2025-05-14 21:09:38 瀏覽:886
落葉片拍攝腳本 發布:2025-05-14 20:40:49 瀏覽:799