国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
Linux多線程編程(不限Linux)

線程?為什么有了進(jìn)程還需要線程呢,他們有什么區(qū)別?使用線程有什么優(yōu)勢(shì)呢?還有多線程編程的一些細(xì)節(jié)問題,如線程之間怎樣同步、互斥,這些東西將在本文中介紹。我在某QQ群里見到這樣一道面試題:

是否熟悉POSIX多線程編程技術(shù)?如熟悉,編寫程序完成如下功能:

1)有一int型全局變量g_Flag初始值為0;

2) 在主線稱中起動(dòng)線程1,打印“this is thread1”,并將g_Flag設(shè)置為1

3) 在主線稱中啟動(dòng)線程2,打印“this is thread2”,并將g_Flag設(shè)置為2

4) 線程序1需要在線程2退出后才能退出

5) 主線程在檢測(cè)到g_Flag從1變?yōu)?,或者從2變?yōu)?的時(shí)候退出

我們帶著這題開始這篇文章,結(jié)束之后,大家就都會(huì)做了。本文的框架如下:

  • 1、進(jìn)程與線程
  • 2、使用線程的理由
  • 3、有關(guān)線程操作的函數(shù)
  • 4、線程之間的互斥
  • 5、線程之間的同步
  • 6、試題最終代碼

1、進(jìn)程與線程

進(jìn)程是程序執(zhí)行時(shí)的一個(gè)實(shí)例,即它是程序已經(jīng)執(zhí)行到課中程度的數(shù)據(jù)結(jié)構(gòu)的匯集。從內(nèi)核的觀點(diǎn)看,進(jìn)程的目的就是擔(dān)當(dāng)分配系統(tǒng)資源(CPU時(shí)間、內(nèi)存等)的基本單位。

線程是進(jìn)程的一個(gè)執(zhí)行流,是CPU調(diào)度和分派的基本單位,它是比進(jìn)程更小的能獨(dú)立運(yùn)行的基本單位。一個(gè)進(jìn)程由幾個(gè)線程組成(擁有很多相對(duì)獨(dú)立的執(zhí)行流的用戶程序共享應(yīng)用程序的大部分?jǐn)?shù)據(jù)結(jié)構(gòu)),線程與同屬一個(gè)進(jìn)程的其他的線程共享進(jìn)程所擁有的全部資源。

"進(jìn)程——資源分配的最小單位,線程——程序執(zhí)行的最小單位"

進(jìn)程有獨(dú)立的地址空間,一個(gè)進(jìn)程崩潰后,在保護(hù)模式下不會(huì)對(duì)其它進(jìn)程產(chǎn)生影響,而線程只是一個(gè)進(jìn)程中的不同執(zhí)行路徑。線程有自己的堆棧和局部變量,但線程沒有單獨(dú)的地址空間,一個(gè)線程死掉就等于整個(gè)進(jìn)程死掉,所以多進(jìn)程的程序要比多線程的程序健壯,但在進(jìn)程切換時(shí),耗費(fèi)資源較大,效率要差一些。但對(duì)于一些要求同時(shí)進(jìn)行并且又要共享某些變量的并發(fā)操作,只能用線程,不能用進(jìn)程。

2、使用線程的理由

從上面我們知道了進(jìn)程與線程的區(qū)別,其實(shí)這些區(qū)別也就是我們使用線程的理由??偟膩碚f就是:進(jìn)程有獨(dú)立的地址空間,線程沒有單獨(dú)的地址空間(同一進(jìn)程內(nèi)的線程共享進(jìn)程的地址空間)。

使用多線程的理由之一是和進(jìn)程相比,它是一種非常"節(jié)儉"的多任務(wù)操作方式。我們知道,在Linux系統(tǒng)下,啟動(dòng)一個(gè)新的進(jìn)程必須分配給它獨(dú)立的地址空間,建立眾多的數(shù)據(jù)表來維護(hù)它的代碼段、堆棧段和數(shù)據(jù)段,這是一種"昂貴"的多任務(wù)工作方式。而運(yùn)行于一個(gè)進(jìn)程中的多個(gè)線程,它們彼此之間使用相同的地址空間,共享大部分?jǐn)?shù)據(jù),啟動(dòng)一個(gè)線程所花費(fèi)的空間遠(yuǎn)遠(yuǎn)小于啟動(dòng)一個(gè)進(jìn)程所花費(fèi)的空間,而且,線程間彼此切換所需的時(shí)間也遠(yuǎn)遠(yuǎn)小于進(jìn)程間切換所需要的時(shí)間。據(jù)統(tǒng)計(jì),總的說來,一個(gè)進(jìn)程的開銷大約是一個(gè)線程開銷的30倍左右,當(dāng)然,在具體的系統(tǒng)上,這個(gè)數(shù)據(jù)可能會(huì)有較大的區(qū)別。

使用多線程的理由之二是線程間方便的通信機(jī)制。對(duì)不同進(jìn)程來說,它們具有獨(dú)立的數(shù)據(jù)空間,要進(jìn)行數(shù)據(jù)的傳遞只能通過通信的方式進(jìn)行,這種方式不僅費(fèi)時(shí),而且很不方便。線程則不然,由于同一進(jìn)程下的線程之間共享數(shù)據(jù)空間,所以一個(gè)線程的數(shù)據(jù)可以直接為其它線程所用,這不僅快捷,而且方便。當(dāng)然,數(shù)據(jù)的共享也帶來其他一些問題,有的變量不能同時(shí)被兩個(gè)線程所修改,有的子程序中聲明為static的數(shù)據(jù)更有可能給多線程程序帶來災(zāi)難性的打擊,這些正是編寫多線程程序時(shí)最需要注意的地方。

除了以上所說的優(yōu)點(diǎn)外,不和進(jìn)程比較,多線程程序作為一種多任務(wù)、并發(fā)的工作方式,當(dāng)然有以下的優(yōu)點(diǎn):

  • 提高應(yīng)用程序響應(yīng)。這對(duì)圖形界面的程序尤其有意義,當(dāng)一個(gè)操作耗時(shí)很長時(shí),整個(gè)系統(tǒng)都會(huì)等待這個(gè)操作,此時(shí)程序不會(huì)響應(yīng)鍵盤、鼠標(biāo)、菜單的操作,而使用多線程技術(shù),將耗時(shí)長的操作(time consuming)置于一個(gè)新的線程,可以避免這種尷尬的情況。
  • 使多CPU系統(tǒng)更加有效。操作系統(tǒng)會(huì)保證當(dāng)線程數(shù)不大于CPU數(shù)目時(shí),不同的線程運(yùn)行于不同的CPU上。
  • 改善程序結(jié)構(gòu)。一個(gè)既長又復(fù)雜的進(jìn)程可以考慮分為多個(gè)線程,成為幾個(gè)獨(dú)立或半獨(dú)立的運(yùn)行部分,這樣的程序會(huì)利于理解和修改。

=============================

從函數(shù)調(diào)用上來說,進(jìn)程創(chuàng)建使用fork()操作;線程創(chuàng)建使用clone()操作。Richard Stevens大師這樣說過:

  • fork is expensive. Memory is copied from the parent to the child, all descriptors are duplicated in the child, and so on. Current implementations use a technique called copy-on-write, which avoids a copy of the parent's data space to the child until the child needs its own copy. But, regardless of this optimization, fork is expensive.

  • IPC is required to pass information between the parent and child after the fork. Passing information from the parent to the child before the fork is easy, since the child starts with a copy of the parent's data space and with a copy of all the parent's descriptors. But, returning information from the child to the parent takes more work.

Threadshelp with both problems. Threads are sometimes called lightweightprocesses since a thread is "lighter weight" than a process. That is,thread creation can be 10–100 times faster than process creation.

Allthreads within a process share the same global memory. This makes thesharing of information easy between the threads, but along with thissimplicity comes the problem of synchronization.

=============================

3、有關(guān)線程操作的函數(shù)

#include <pthread.h>

int pthread_create(pthread_t *tid, const pthread_attr_t *attr, void *(*func) (void *), void *arg);
int pthread_join (pthread_t tid, void ** status);
pthread_t pthread_self (void);
int pthread_detach (pthread_t tid);
void pthread_exit (void *status);

pthread_create用于創(chuàng)建一個(gè)線程,成功返回0,否則返回Exxx(為正數(shù))。

  • pthread_t *tid:線程id的類型為pthread_t,通常為無符號(hào)整型,當(dāng)調(diào)用pthread_create成功時(shí),通過*tid指針返回。
  • const pthread_attr_t *attr:指定創(chuàng)建線程的屬性,如線程優(yōu)先級(jí)、初始棧大小、是否為守護(hù)進(jìn)程等??梢允褂肗ULL來使用默認(rèn)值,通常情況下我們都是使用默認(rèn)值。
  • void *(*func) (void *):函數(shù)指針func,指定當(dāng)新的線程創(chuàng)建之后,將執(zhí)行的函數(shù)。
  • void *arg:線程將執(zhí)行的函數(shù)的參數(shù)。如果想傳遞多個(gè)參數(shù),請(qǐng)將它們封裝在一個(gè)結(jié)構(gòu)體中。

pthread_join用于等待某個(gè)線程退出,成功返回0,否則返回Exxx(為正數(shù))。

  • pthread_t tid:指定要等待的線程ID
  • void ** status:如果不為NULL,那么線程的返回值存儲(chǔ)在status指向的空間中(這就是為什么status是二級(jí)指針的原因!這種才參數(shù)也稱為“值-結(jié)果”參數(shù))。

pthread_self用于返回當(dāng)前線程的ID。

pthread_detach用于是指定線程變?yōu)?strong>分離狀態(tài),就像進(jìn)程脫離終端而變?yōu)楹笈_(tái)進(jìn)程類似。成功返回0,否則返回Exxx(為正數(shù))。變?yōu)榉蛛x狀態(tài)的線程,如果線程退出,它的所有資源將全部釋放。而如果不是分離狀態(tài),線程必須保留它的線程ID,退出狀態(tài)直到其它線程對(duì)它調(diào)用了pthread_join。

進(jìn)程也是類似,這也是當(dāng)我們打開進(jìn)程管理器的時(shí)候,發(fā)現(xiàn)有很多僵死進(jìn)程的原因!也是為什么一定要有僵死這個(gè)進(jìn)程狀態(tài)。

pthread_exit用于終止線程,可以指定返回值,以便其他線程通過pthread_join函數(shù)獲取該線程的返回值。

  • void *status:指針線程終止的返回值。

知道了這些函數(shù)之后,我們?cè)噲D來完成本文一開始的問題:

1)有一int型全局變量g_Flag初始值為0;

2)在主線稱中起動(dòng)線程1,打印“this is thread1”,并將g_Flag設(shè)置為1

3)在主線稱中啟動(dòng)線程2,打印“this is thread2”,并將g_Flag設(shè)置為2

這3點(diǎn)很簡單嘛?。?!不就是調(diào)用pthread_create創(chuàng)建線程。代碼如下:

/*
* 1)有一int型全局變量g_Flag初始值為0;
*
* 2)在主線稱中起動(dòng)線程1,打印“this is thread1”,并將g_Flag設(shè)置為1
*
* 3)在主線稱中啟動(dòng)線程2,打印“this is thread2”,并將g_Flag設(shè)置為2
*
*/

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

int g_Flag=0;

void* thread1(void*);
void* thread2(void*);

/*
* when program is started, a single thread is created, called the initial thread or main thread.
* Additional threads are created by pthread_create.
* So we just need to create two thread in main().
*/

int main(int argc, char** argv)
{
printf("enter main\n");
pthread_t tid1, tid2;
int rc1=0, rc2=0;
rc2 = pthread_create(&tid2, NULL, thread2, NULL);
if(rc2 != 0)
printf("%s: %d\n",__func__, strerror(rc2));

rc1 = pthread_create(&tid1, NULL, thread1, &tid2);
if(rc1 != 0)
printf("%s: %d\n",__func__, strerror(rc1));
printf("leave main\n");
exit(0);
}
/*
* thread1() will be execute by thread1, after pthread_create()
* it will set g_Flag = 1;
*/

void* thread1(void* arg)
{
printf("enter thread1\n");
printf("this is thread1, g_Flag: %d, thread id is %u\n",g_Flag, (unsigned int)pthread_self());
g_Flag = 1;
printf("this is thread1, g_Flag: %d, thread id is %u\n",g_Flag, (unsigned int)pthread_self());
printf("leave thread1\n");
pthread_exit(0);
}

/*
* thread2() will be execute by thread2, after pthread_create()
* it will set g_Flag = 2;
*/

void* thread2(void* arg)
{
printf("enter thread2\n");
printf("this is thread2, g_Flag: %d, thread id is %u\n",g_Flag, (unsigned int)pthread_self());
g_Flag = 2;
printf("this is thread1, g_Flag: %d, thread id is %u\n",g_Flag, (unsigned int)pthread_self());
printf("leave thread2\n");
pthread_exit(0);
}

這樣就完成了1)、2)、3)這三點(diǎn)要求。編譯執(zhí)行得如下結(jié)果:

netsky@ubuntu:~/workspace/pthead_test$ gcc -lpthread test.c

如果程序中使用到了pthread庫中的函數(shù),除了要#include<pthread.h>,在編譯的時(shí)候還有加上-lpthread 選項(xiàng)。
netsky@ubuntu:~/workspace/pthead_test$ ./a.out
enter main
enter thread2
this is thread2, g_Flag: 0, thread id is 3079588720
this is thread1, g_Flag: 2, thread id is 3079588720
leave thread2
leave main
enter thread1
this is thread1, g_Flag: 2, thread id is 3071196016
this is thread1, g_Flag: 1, thread id is 3071196016
leave thread1
但是運(yùn)行結(jié)果不一定是上面的,還有可能是:

netsky@ubuntu:~/workspace/pthead_test$ ./a.out
enter main
leave main
enter thread1
this is thread1, g_Flag: 0, thread id is 3069176688
this is thread1, g_Flag: 1, thread id is 3069176688
leave thread1

或者是:

netsky@ubuntu:~/workspace/pthead_test$ ./a.out
enter main
leave main
等等。這也很好理解因?yàn)椋@取決于主線程main函數(shù)何時(shí)終止,線程thread1、thread2是否能夠來得急執(zhí)行它們的函數(shù)。這也是多線程編程時(shí)要注意的問題,因?yàn)橛锌赡芤粋€(gè)線程會(huì)影響到整個(gè)進(jìn)程中的所有其它線程!如果我們?cè)趍ain函數(shù)退出前,sleep()一段時(shí)間,就可以保證thread1、thread2來得及執(zhí)行。

Attention:大家肯定已經(jīng)注意到了,我們?cè)诰€程函數(shù)thread1()、thread2()執(zhí)行完之前都調(diào)用了pthread_exit。如果我是調(diào)用exit()又或者是return會(huì)怎樣呢?自己動(dòng)手試試吧!

pthread_exit()用于線程退出,可以指定返回值,以便其他線程通過pthread_join()函數(shù)獲取該線程的返回值。
return是函數(shù)返回,只有線程函數(shù)return,線程才會(huì)退出。
exit是進(jìn)程退出,如果在線程函數(shù)中調(diào)用exit,進(jìn)程中的所有函數(shù)都會(huì)退出!

“4) 線程序1需要在線程2退出后才能退出”第4點(diǎn)也很容易解決,直接在thread1的函數(shù)退出之前調(diào)用pthread_join就OK了。

4、線程之間的互斥

上面的代碼似乎很好的解決了問題的前面4點(diǎn)要求,其實(shí)不然?。。∫?yàn)間_Flag是一個(gè)全局變量,線程thread1和thread2可以同時(shí)對(duì)它進(jìn)行操作,需要對(duì)它進(jìn)行加鎖保護(hù),thread1和thread2要互斥訪問才行。下面我們就介紹如何加鎖保護(hù)——互斥鎖。

互斥鎖:

使用互斥鎖(互斥)可以使線程按順序執(zhí)行。通常,互斥鎖通過確保一次只有一個(gè)線程執(zhí)行代碼的臨界段來同步多個(gè)線程?;コ怄i還可以保護(hù)單線程代碼。

互斥鎖的相關(guān)操作函數(shù)如下:

#include <pthread.h> 

int pthread_mutex_lock(pthread_mutex_t * mptr);
int pthread_mutex_unlock(pthread_mutex_t * mptr);
//Both return: 0 if OK, positive Exxx value on error

在對(duì)臨界資源進(jìn)行操作之前需要pthread_mutex_lock先加鎖,操作完之后pthread_mutex_unlock再解鎖。而且在這之前需要聲明一個(gè)pthread_mutex_t類型的變量,用作前面兩個(gè)函數(shù)的參數(shù)。具體代碼見第5節(jié)。

5、線程之間的同步

第5點(diǎn)——主線程在檢測(cè)到g_Flag從1變?yōu)?,或者從2變?yōu)?的時(shí)候退出。就需要用到線程同步技術(shù)!線程同步需要條件變量。

條件變量:

使用條件變量可以以原子方式阻塞線程,直到某個(gè)特定條件為真為止。條件變量始終與互斥鎖一起使用。對(duì)條件的測(cè)試是在互斥鎖(互斥)的保護(hù)下進(jìn)行的。

如果條件為假,線程通常會(huì)基于條件變量阻塞,并以原子方式釋放等待條件變化的互斥鎖。如果另一個(gè)線程更改了條件,該線程可能會(huì)向相關(guān)的條件變量發(fā)出信號(hào),從而使一個(gè)或多個(gè)等待的線程執(zhí)行以下操作:

  • 喚醒
  • 再次獲取互斥鎖
  • 重新評(píng)估條件

在以下情況下,條件變量可用于在進(jìn)程之間同步線程:

  • 線程是在可以寫入的內(nèi)存中分配的
  • 內(nèi)存由協(xié)作進(jìn)程共享

使用條件變量可以以原子方式阻塞線程,直到某個(gè)特定條件為真為止。”即可用到第5點(diǎn),主線程main函數(shù)阻塞于等待g_Flag從1變?yōu)?,或者從2變?yōu)?。條件變量的相關(guān)函數(shù)如下:

#include <pthread.h>

int pthread_cond_wait(pthread_cond_t *cptr, pthread_mutex_t *mptr);
int pthread_cond_signal(pthread_cond_t *cptr);
//Both return: 0 if OK, positive Exxx value on error

pthread_cond_wait用于等待某個(gè)特定的條件為真,pthread_cond_signal用于通知阻塞的線程某個(gè)特定的條件為真了。在調(diào)用者兩個(gè)函數(shù)之前需要聲明一個(gè)pthread_cond_t類型的變量,用于這兩個(gè)函數(shù)的參數(shù)。

為什么條件變量始終與互斥鎖一起使用,對(duì)條件的測(cè)試是在互斥鎖(互斥)的保護(hù)下進(jìn)行的呢?因?yàn)椤澳硞€(gè)特性條件”通常是在多個(gè)線程之間共享的某個(gè)變量?;コ怄i允許這個(gè)變量可以在不同的線程中設(shè)置和檢測(cè)。

通常,pthread_cond_wait只是喚醒等待某個(gè)條件變量的一個(gè)線程。如果需要喚醒所有等待某個(gè)條件變量的線程,需要調(diào)用:

int pthread_cond_broadcast (pthread_cond_t * cptr);

默認(rèn)情況下面,阻塞的線程會(huì)一直等待,知道某個(gè)條件變量為真。如果想設(shè)置最大的阻塞時(shí)間可以調(diào)用:

int pthread_cond_timedwait (pthread_cond_t * cptr, pthread_mutex_t *mptr, const struct timespec *abstime);

如果時(shí)間到了,條件變量還沒有為真,仍然返回,返回值為ETIME。

6、試題最終代碼

通過前面的介紹,我們可以輕松的寫出代碼了,如下所示:

/*
是否熟悉POSIX多線程編程技術(shù)?如熟悉,編寫程序完成如下功能:
1)有一int型全局變量g_Flag初始值為0;
2)在主線稱中起動(dòng)線程1,打印“this is thread1”,并將g_Flag設(shè)置為1
3)在主線稱中啟動(dòng)線程2,打印“this is thread2”,并將g_Flag設(shè)置為2
4)線程序1需要在線程2退出后才能退出
5)主線程在檢測(cè)到g_Flag從1變?yōu)?,或者從2變?yōu)?的時(shí)候退出
*/

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

typedef void* (*fun)(void*);

int g_Flag=0;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

void* thread1(void*);
void* thread2(void*);

/*
* when program is started, a single thread is created, called the initial thread or main thread.
* Additional threads are created by pthread_create.
* So we just need to create two thread in main().
*/


int main(int argc, char** argv)
{
printf("enter main\n");
pthread_t tid1, tid2;
int rc1=0, rc2=0;
rc2 = pthread_create(&tid2, NULL, thread2, NULL);
if(rc2 != 0)
printf("%s: %d\n",__func__, strerror(rc2));

rc1 = pthread_create(&tid1, NULL, thread1, &tid2);
if(rc1 != 0)
printf("%s: %d\n",__func__, strerror(rc1));

pthread_cond_wait(&cond, &mutex);
printf("leave main\n");
exit(0);
}

/*
* thread1() will be execute by thread1, after pthread_create()
* it will set g_Flag = 1;
*/

void* thread1(void* arg)
{
printf("enter thread1\n");
printf("this is thread1, g_Flag: %d, thread id is %u\n",g_Flag, (unsigned int)pthread_self());
pthread_mutex_lock(&mutex);
if(g_Flag == 2)
pthread_cond_signal(&cond);
g_Flag = 1;
printf("this is thread1, g_Flag: %d, thread id is %u\n",g_Flag, (unsigned int)pthread_self());
pthread_mutex_unlock(&mutex);
pthread_join(*(pthread_t*)arg, NULL);
printf("leave thread1\n");
pthread_exit(0);
}

/*
* thread2() will be execute by thread2, after pthread_create()
* it will set g_Flag = 2;
*/

void* thread2(void* arg)
{
printf("enter thread2\n");
printf("this is thread2, g_Flag: %d, thread id is %u\n",g_Flag, (unsigned int)pthread_self());
pthread_mutex_lock(&mutex);
if(g_Flag == 1)
pthread_cond_signal(&cond);
g_Flag = 2;
printf("this is thread1, g_Flag: %d, thread id is %u\n",g_Flag, (unsigned int)pthread_self());
pthread_mutex_unlock(&mutex);
printf("leave thread2\n");
pthread_exit(0);
}
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
多線程加鎖
Linux下C語言的多線程編程學(xué)習(xí)
linux下C語言多線程編程實(shí)例
linux C 多線程/線程池編程 同步實(shí)例
Linux中線程的掛起與恢復(fù)(進(jìn)程暫停)
linux多線程編程,你還在用sleep么?用pthread
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服