青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

ivy-jie

progress ...

C++博客 首頁 新隨筆 聯系 聚合 管理
  9 Posts :: 41 Stories :: 6 Comments :: 0 Trackbacks
  Linux系統下的多線程遵循POSIX線程接口,稱為pthread。編寫Linux下的多線程程序,需要使用頭文件pthread.h,連接時需要使用庫libpthread.a。順便說一下,Linux下pthread的實現是通過系統調用clone()來實現的。clone()是Linux所特有的系統調用,它的使用方式類似fork,關于clone()的詳細情況,有興趣的讀者可以去查看有關文檔說明。下面我們展示一個最簡單的多線程程序example1.c。

/* example.c*/
#include <stdio.h>
#include <pthread.h>
void thread(void)
{
 int i;
 for(i=0;i<3;i++)
  printf("This is a pthread.n");
}

int main(void)
{
 pthread_t id;
 int i,ret;
 ret=pthread_create(&id,NULL,(void *) thread,NULL);
 if(ret!=0){
  printf ("Create pthread error!n");
  exit (1);
 }
 for(i=0;i<3;i++)
  printf("This is the main process.n");
 pthread_join(id,NULL);
 return (0);
}

  我們編譯此程序:

gcc example1.c -lpthread -o example1

  運行example1,我們得到如下結果:

This is the main process.
This is a pthread.
This is the main process.
This is the main process.
This is a pthread.
This is a pthread.

  再次運行,我們可能得到如下結果:

This is a pthread.
This is the main process.
This is a pthread.
This is the main process.
This is a pthread.
This is the main process.

  前后兩次結果不一樣,這是兩個線程爭奪CPU資源的結果。上面的示例中,我們使用到了兩個函數,pthread_create和pthread_join,并聲明了一個pthread_t型的變量。

  pthread_t在頭文件/usr/include/bits/pthreadtypes.h中定義:

  typedef unsigned long int pthread_t;

  它是一個線程的標識符。函數pthread_create用來創建一個線程,它的原型為:

extern int pthread_create __P ((pthread_t *__thread, __const pthread_attr_t *__attr,void *(*__start_routine) (void *), void *__arg));

  第一個參數為指向線程標識符的指針,第二個參數用來設置線程屬性,第三個參數是線程運行函數的起始地址,最后一個參數是運行函數的參數。這里,我們的函數thread不需要參數,所以最后一個參數設為空指針。第二個參數我們也設為空指針,這樣將生成默認屬性的線程。對線程屬性的設定和修改我們將在下一節闡述。當創建線程成功時,函數返回0,若不為0則說明創建線程失敗,常見的錯誤返回代碼為EAGAIN和EINVAL。前者表示系統限制創建新的線程,例如線程數目過多了;后者表示第二個參數代表的線程屬性值非法。創建線程成功后,新創建的線程則運行參數三和參數四確定的函數,原來的線程則繼續運行下一行代碼。

  函數pthread_join用來等待一個線程的結束。函數原型為:

  extern int pthread_join __P ((pthread_t __th, void **__thread_return));

  第一個參數為被等待的線程標識符,第二個參數為一個用戶定義的指針,它可以用來存儲被等待線程的返回值。這個函數是一個線程阻塞的函數,調用它的函數將一直等待到被等待的線程結束為止,當函數返回時,被等待線程的資源被收回。一個線程的結束有兩種途徑,一種是象我們上面的例子一樣,函數結束了,調用它的線程也就結束了;另一種方式是通過函數pthread_exit來實現。它的函數原型為:

  extern void pthread_exit __P ((void *__retval)) __attribute__ ((__noreturn__));

  唯一的參數是函數的返回代碼,只要pthread_join中的第二個參數thread_return不是NULL,這個值將被傳遞給thread_return。最后要說明的是,一個線程不能被多個線程等待,否則第一個接收到信號的線程成功返回,其余調用pthread_join的線程則返回錯誤代碼ESRCH。

  在這一節里,我們編寫了一個最簡單的線程,并掌握了最常用的三個函數pthread_create,pthread_join和pthread_exit。下面,我們來了解線程的一些常用屬性以及如何設置這些屬性。

  修改線程的屬性

  在上一節的例子里,我們用pthread_create函數創建了一個線程,在這個線程中,我們使用了默認參數,即將該函數的第二個參數設為NULL。的確,對大多數程序來說,使用默認屬性就夠了,但我們還是有必要來了解一下線程的有關屬性。

  屬性結構為pthread_attr_t,它同樣在頭文件/usr/include/pthread.h中定義,喜歡追根問底的人可以自己去查看。屬性值不能直接設置,須使用相關函數進行操作,初始化的函數為pthread_attr_init,這個函數必須在pthread_create函數之前調用。屬性對象主要包括是否綁定、是否分離、堆棧地址、堆棧大小、優先級。默認的屬性為非綁定、非分離、缺省1M的堆棧、與父進程同樣級別的優先級。

  關于線程的綁定,牽涉到另外一個概念:輕進程(LWP:Light Weight Process)。輕進程可以理解為內核線程,它位于用戶層和系統層之間。系統對線程資源的分配、對線程的控制是通過輕進程來實現的,一個輕進程可以控制一個或多個線程。默認狀況下,啟動多少輕進程、哪些輕進程來控制哪些線程是由系統來控制的,這種狀況即稱為非綁定的。綁定狀況下,則顧名思義,即某個線程固定的"綁"在一個輕進程之上。被綁定的線程具有較高的響應速度,這是因為CPU時間片的調度是面向輕進程的,綁定的線程可以保證在需要的時候它總有一個輕進程可用。通過設置被綁定的輕進程的優先級和調度級可以使得綁定的線程滿足諸如實時反應之類的要求。

  設置線程綁定狀態的函數為pthread_attr_setscope,它有兩個參數,第一個是指向屬性結構的指針,第二個是綁定類型,它有兩個取值:PTHREAD_SCOPE_SYSTEM(綁定的)和PTHREAD_SCOPE_PROCESS(非綁定的)。下面的代碼即創建了一個綁定的線程。

#include <pthread.h>
pthread_attr_t attr;
pthread_t tid;

/*初始化屬性值,均設為默認值*/
pthread_attr_init(&attr);
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);

pthread_create(&tid, &attr, (void *) my_function, NULL);

  線程的分離狀態決定一個線程以什么樣的方式來終止自己。在上面的例子中,我們采用了線程的默認屬性,即為非分離狀態,這種情況下,原有的線程等待創建的線程結束。只有當pthread_join()函數返回時,創建的線程才算終止,才能釋放自己占用的系統資源。而分離線程不是這樣子的,它沒有被其他的線程所等待,自己運行結束了,線程也就終止了,馬上釋放系統資源。程序員應該根據自己的需要,選擇適當的分離狀態。設置線程分離狀態的函數為pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)。第二個參數可選為PTHREAD_CREATE_DETACHED(分離線程)和 PTHREAD _CREATE_JOINABLE(非分離線程)。這里要注意的一點是,如果設置一個線程為分離線程,而這個線程運行又非常快,它很可能在pthread_create函數返回之前就終止了,它終止以后就可能將線程號和系統資源移交給其他的線程使用,這樣調用pthread_create的線程就得到了錯誤的線程號。要避免這種情況可以采取一定的同步措施,最簡單的方法之一是可以在被創建的線程里調用pthread_cond_timewait函數,讓這個線程等待一會兒,留出足夠的時間讓函數pthread_create返回。設置一段等待時間,是在多線程編程里常用的方法。但是注意不要使用諸如wait()之類的函數,它們是使整個進程睡眠,并不能解決線程同步的問題。

  另外一個可能常用的屬性是線程的優先級,它存放在結構sched_param中。用函數pthread_attr_getschedparam和函數pthread_attr_setschedparam進行存放,一般說來,我們總是先取優先級,對取得的值修改后再存放回去。下面即是一段簡單的例子。

#include <pthread.h>
#include <sched.h>
pthread_attr_t attr;
pthread_t tid;
sched_param param;
int newprio=20;

pthread_attr_init(&attr);
pthread_attr_getschedparam(&attr, &param);
param.sched_priority=newprio;
pthread_attr_setschedparam(&attr, &param);
pthread_create(&tid, &attr, (void *)myfunction, myarg);
線程的數據處理

  和進程相比,線程的最大優點之一是數據的共享性,各個進程共享父進程處沿襲的數據段,可以方便的獲得、修改數據。但這也給多線程編程帶來了許多問題。我們必須當心有多個不同的進程訪問相同的變量。許多函數是不可重入的,即同時不能運行一個函數的多個拷貝(除非使用不同的數據段)。在函數中聲明的靜態變量常常帶來問題,函數的返回值也會有問題。因為如果返回的是函數內部靜態聲明的空間的地址,則在一個線程調用該函數得到地址后使用該地址指向的數據時,別的線程可能調用此函數并修改了這一段數據。在進程中共享的變量必須用關鍵字volatile來定義,這是為了防止編譯器在優化時(如gcc中使用-OX參數)改變它們的使用方式。為了保護變量,我們必須使用信號量、互斥等方法來保證我們對變量的正確使用。下面,我們就逐步介紹處理線程數據時的有關知識。

  1、線程數據

  在單線程的程序里,有兩種基本的數據:全局變量和局部變量。但在多線程程序里,還有第三種數據類型:線程數據(TSD: Thread-Specific Data)。它和全局變量很象,在線程內部,各個函數可以象使用全局變量一樣調用它,但它對線程外部的其它線程是不可見的。這種數據的必要性是顯而易見的。例如我們常見的變量errno,它返回標準的出錯信息。它顯然不能是一個局部變量,幾乎每個函數都應該可以調用它;但它又不能是一個全局變量,否則在A線程里輸出的很可能是B線程的出錯信息。要實現諸如此類的變量,我們就必須使用線程數據。我們為每個線程數據創建一個鍵,它和這個鍵相關聯,在各個線程里,都使用這個鍵來指代線程數據,但在不同的線程里,這個鍵代表的數據是不同的,在同一個線程里,它代表同樣的數據內容。

  和線程數據相關的函數主要有4個:創建一個鍵;為一個鍵指定線程數據;從一個鍵讀取線程數據;刪除鍵。

  創建鍵的函數原型為:

extern int pthread_key_create __P ((pthread_key_t *__key,void (*__destr_function) (void *)));
  
  第一個參數為指向一個鍵值的指針,第二個參數指明了一個destructor函數,如果這個參數不為空,那么當每個線程結束時,系統將調用這個函數來釋放綁定在這個鍵上的內存塊。這個函數常和函數pthread_once ((pthread_once_t*once_control, void (*initroutine) (void)))一起使用,為了讓這個鍵只被創建一次。函數pthread_once聲明一個初始化函數,第一次調用pthread_once時它執行這個函數,以后的調用將被它忽略。

  在下面的例子中,我們創建一個鍵,并將它和某個數據相關聯。我們要定義一個函數createWindow,這個函數定義一個圖形窗口(數據類型為Fl_Window *,這是圖形界面開發工具FLTK中的數據類型)。由于各個線程都會調用這個函數,所以我們使用線程數據。

/* 聲明一個鍵*/
pthread_key_t myWinKey;
/* 函數 createWindow */
void createWindow ( void ) {
 Fl_Window * win;
 static pthread_once_t once= PTHREAD_ONCE_INIT;
 /* 調用函數createMyKey,創建鍵*/
 pthread_once ( & once, createMyKey) ;
 /*win指向一個新建立的窗口*/
 win=new Fl_Window( 0, 0, 100, 100, "MyWindow");
 /* 對此窗口作一些可能的設置工作,如大小、位置、名稱等*/
 setWindow(win);
 /* 將窗口指針值綁定在鍵myWinKey上*/
 pthread_setpecific ( myWinKey, win);
}

/* 函數 createMyKey,創建一個鍵,并指定了destructor */
void createMyKey ( void ) {
 pthread_keycreate(&myWinKey, freeWinKey);
}

/* 函數 freeWinKey,釋放空間*/
void freeWinKey ( Fl_Window * win){
 delete win;
}

  這樣,在不同的線程中調用函數createMyWin,都可以得到在線程內部均可見的窗口變量,這個變量通過函數pthread_getspecific得到。在上面的例子中,我們已經使用了函數pthread_setspecific來將線程數據和一個鍵綁定在一起。這兩個函數的原型如下:

  extern int pthread_setspecific __P ((pthread_key_t __key,__const void *__pointer));
  extern void *pthread_getspecific __P ((pthread_key_t __key));

  這兩個函數的參數意義和使用方法是顯而易見的。要注意的是,用pthread_setspecific為一個鍵指定新的線程數據時,必須自己釋放原有的線程數據以回收空間。這個過程函數pthread_key_delete用來刪除一個鍵,這個鍵占用的內存將被釋放,但同樣要注意的是,它只釋放鍵占用的內存,并不釋放該鍵關聯的線程數據所占用的內存資源,而且它也不會觸發函數pthread_key_create中定義的destructor函數。線程數據的釋放必須在釋放鍵之前完成。

  2、互斥鎖

  互斥鎖用來保證一段時間內只有一個線程在執行一段代碼。必要性顯而易見:假設各個線程向同一個文件順序寫入數據,最后得到的結果一定是災難性的。

  我們先看下面一段代碼。這是一個讀/寫程序,它們公用一個緩沖區,并且我們假定一個緩沖區只能保存一條信息。即緩沖區只有兩個狀態:有信息或沒有信息。

void reader_function ( void );
void writer_function ( void );

char buffer;
int buffer_has_item=0;
pthread_mutex_t mutex;
struct timespec delay;
void main ( void ){
 pthread_t reader;
 /* 定義延遲時間*/
 delay.tv_sec = 2;
 delay.tv_nec = 0;
 /* 用默認屬性初始化一個互斥鎖對象*/
 pthread_mutex_init (&mutex,NULL);
 pthread_create(&reader, pthread_attr_default, (void *)&reader_function), NULL);
 writer_function( );
}

void writer_function (void){
 while(1){
  /* 鎖定互斥鎖*/
  pthread_mutex_lock (&mutex);
  if (buffer_has_item==0){
   buffer=make_new_item( );
   buffer_has_item=1;
  }
  /* 打開互斥鎖*/
  pthread_mutex_unlock(&mutex);
  pthread_delay_np(&delay);
 }
}

void reader_function(void){
 while(1){
  pthread_mutex_lock(&mutex);
  if(buffer_has_item==1){
   consume_item(buffer);
   buffer_has_item=0;
  }
  pthread_mutex_unlock(&mutex);
  pthread_delay_np(&delay);
 }
}

  這里聲明了互斥鎖變量mutex,結構pthread_mutex_t為不公開的數據類型,其中包含一個系統分配的屬性對象。函數pthread_mutex_init用來生成一個互斥鎖。NULL參數表明使用默認屬性。如果需要聲明特定屬性的互斥鎖,須調用函數pthread_mutexattr_init。函數pthread_mutexattr_setpshared和函數pthread_mutexattr_settype用來設置互斥鎖屬性。前一個函數設置屬性pshared,它有兩個取值,PTHREAD_PROCESS_PRIVATE和PTHREAD_PROCESS_SHARED。前者用來不同進程中的線程同步,后者用于同步本進程的不同線程。在上面的例子中,我們使用的是默認屬性PTHREAD_PROCESS_ PRIVATE。后者用來設置互斥鎖類型,可選的類型有PTHREAD_MUTEX_NORMAL、PTHREAD_MUTEX_ERRORCHECK、PTHREAD_MUTEX_RECURSIVE和PTHREAD _MUTEX_DEFAULT。它們分別定義了不同的上所、解鎖機制,一般情況下,選用最后一個默認屬性。

  pthread_mutex_lock聲明開始用互斥鎖上鎖,此后的代碼直至調用pthread_mutex_unlock為止,均被上鎖,即同一時間只能被一個線程調用執行。當一個線程執行到pthread_mutex_lock處時,如果該鎖此時被另一個線程使用,那此線程被阻塞,即程序將等待到另一個線程釋放此互斥鎖。在上面的例子中,我們使用了pthread_delay_np函數,讓線程睡眠一段時間,就是為了防止一個線程始終占據此函數。

  上面的例子非常簡單,就不再介紹了,需要提出的是在使用互斥鎖的過程中很有可能會出現死鎖:兩個線程試圖同時占用兩個資源,并按不同的次序鎖定相應的互斥鎖,例如兩個線程都需要鎖定互斥鎖1和互斥鎖2,a線程先鎖定互斥鎖1,b線程先鎖定互斥鎖2,這時就出現了死鎖。此時我們可以使用函數pthread_mutex_trylock,它是函數pthread_mutex_lock的非阻塞版本,當它發現死鎖不可避免時,它會返回相應的信息,程序員可以針對死鎖做出相應的處理。另外不同的互斥鎖類型對死鎖的處理不一樣,但最主要的還是要程序員自己在程序設計注意這一點。

  3、條件變量

  前一節中我們講述了如何使用互斥鎖來實現線程間數據的共享和通信,互斥鎖一個明顯的缺點是它只有兩種狀態:鎖定和非鎖定。而條件變量通過允許線程阻塞和等待另一個線程發送信號的方法彌補了互斥鎖的不足,它常和互斥鎖一起使用。使用時,條件變量被用來阻塞一個線程,當條件不滿足時,線程往往解開相應的互斥鎖并等待條件發生變化。一旦其它的某個線程改變了條件變量,它將通知相應的條件變量喚醒一個或多個正被此條件變量阻塞的線程。這些線程將重新鎖定互斥鎖并重新測試條件是否滿足。一般說來,條件變量被用來進行線承間的同步。

  條件變量的結構為pthread_cond_t,函數pthread_cond_init()被用來初始化一個條件變量。它的原型為:

  extern int pthread_cond_init __P ((pthread_cond_t *__cond,__const pthread_condattr_t *__cond_attr));

  其中cond是一個指向結構pthread_cond_t的指針,cond_attr是一個指向結構pthread_condattr_t的指針。結構pthread_condattr_t是條件變量的屬性結構,和互斥鎖一樣我們可以用它來設置條件變量是進程內可用還是進程間可用,默認值是PTHREAD_ PROCESS_PRIVATE,即此條件變量被同一進程內的各個線程使用。注意初始化條件變量只有未被使用時才能重新初始化或被釋放。釋放一個條件變量的函數為pthread_cond_ destroy(pthread_cond_t cond)。 

  函數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喚醒,但是要注意的是,條件變量只是起阻塞和喚醒線程的作用,具體的判斷條件還需用戶給出,例如一個變量是否為0等等,這一點我們從后面的例子中可以看到。線程被喚醒后,它將重新檢查判斷條件是否滿足,如果還不滿足,一般說來線程應該仍阻塞在這里,被等待被下一次喚醒。這個過程一般用while語句實現。

  另一個用來阻塞線程的函數是pthread_cond_timedwait(),它的原型為:
  extern int pthread_cond_timedwait __P ((pthread_cond_t *__cond,pthread_mutex_t *__mutex, __const struct timespec *__abstime));

  它比函數pthread_cond_wait()多了一個時間參數,經歷abstime段時間后,即使條件變量不滿足,阻塞也被解除。

  函數pthread_cond_signal()的原型為:

  extern int pthread_cond_signal __P ((pthread_cond_t *__cond));

  它用來釋放被阻塞在條件變量cond上的一個線程。多個線程阻塞在此條件變量上時,哪一個線程被喚醒是由線程的調度策略所決定的。要注意的是,必須用保護條件變量的互斥鎖來保護這個函數,否則條件滿足信號又可能在測試條件和調用pthread_cond_wait函數之間被發出,從而造成無限制的等待。下面是使用函數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);
}

  count值為0時,decrement函數在pthread_cond_wait處被阻塞,并打開互斥鎖count_lock。此時,當調用到函數increment_count時,pthread_cond_signal()函數改變條件變量,告知decrement_count()停止阻塞。讀者可以試著讓兩個線程分別運行這兩個函數,看看會出現什么樣的結果。

  函數pthread_cond_broadcast(pthread_cond_t *cond)用來喚醒所有被阻塞在條件變量cond上的線程。這些線程被喚醒后將再次競爭相應的互斥鎖,所以必須小心使用這個函數。

  4、信號量

  信號量本質上是一個非負的整數計數器,它被用來控制對公共資源的訪問。當公共資源增加時,調用函數sem_post()增加信號量。只有當信號量值大于0時,才能使用公共資源,使用后,函數sem_wait()減少信號量。函數sem_trywait()和函數pthread_ mutex_trylock()起同樣的作用,它是函數sem_wait()的非阻塞版本。下面我們逐個介紹和信號量有關的一些函數,它們都在頭文件/usr/include/semaphore.h中定義。

  信號量的數據類型為結構sem_t,它本質上是一個長整型的數。函數sem_init()用來初始化一個信號量。它的原型為:

  extern int sem_init __P ((sem_t *__sem, int __pshared, unsigned int __value));

  sem為指向信號量結構的一個指針;pshared不為0時此信號量在進程間共享,否則只能為當前進程的所有線程共享;value給出了信號量的初始值。

  函數sem_post( sem_t *sem )用來增加信號量的值。當有線程阻塞在這個信號量上時,調用這個函數會使其中的一個線程不在阻塞,選擇機制同樣是由線程的調度策略決定的。

  函數sem_wait( sem_t *sem )被用來阻塞當前線程直到信號量sem的值大于0,解除阻塞后將sem的值減一,表明公共資源經使用后減少。函數sem_trywait ( sem_t *sem )是函數sem_wait()的非阻塞版本,它直接將信號量sem的值減一。

  函數sem_destroy(sem_t *sem)用來釋放信號量sem。

  下面我們來看一個使用信號量的例子。在這個例子中,一共有4個線程,其中兩個線程負責從文件讀取數據到公共的緩沖區,另兩個線程從緩沖區讀取數據作不同的處理(加和乘運算)。

/* File sem.c */
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#define MAXSTACK 100
int stack[MAXSTACK][2];
int size=0;
sem_t sem;
/* 從文件1.dat讀取數據,每讀一次,信號量加一*/
void ReadData1(void){
 FILE *fp=fopen("1.dat","r");
 while(!feof(fp)){
  fscanf(fp,"%d %d",&stack[size][0],&stack[size][1]);
  sem_post(&sem);
  ++size;
 }
 fclose(fp);
}
/*從文件2.dat讀取數據*/
void ReadData2(void){
 FILE *fp=fopen("2.dat","r");
 while(!feof(fp)){
  fscanf(fp,"%d %d",&stack[size][0],&stack[size][1]);
  sem_post(&sem);
  ++size;
 }
 fclose(fp);
}
/*阻塞等待緩沖區有數據,讀取數據后,釋放空間,繼續等待*/
void HandleData1(void){
 while(1){
  sem_wait(&sem);
  printf("Plus:%d+%d=%dn",stack[size][0],stack[size][1],
  stack[size][0]+stack[size][1]);
  --size;
 }
}

void HandleData2(void){
 while(1){
  sem_wait(&sem);
  printf("Multiply:%d*%d=%dn",stack[size][0],stack[size][1],
  stack[size][0]*stack[size][1]);
  --size;
 }
}
int main(void){
 pthread_t t1,t2,t3,t4;
 sem_init(&sem,0,0);
 pthread_create(&t1,NULL,(void *)HandleData1,NULL);
 pthread_create(&t2,NULL,(void *)HandleData2,NULL);
 pthread_create(&t3,NULL,(void *)ReadData1,NULL);
 pthread_create(&t4,NULL,(void *)ReadData2,NULL);
 /* 防止程序過早退出,讓它在此無限期等待*/
 pthread_join(t1,NULL);
}

  在Linux下,我們用命令gcc -lpthread sem.c -o sem生成可執行文件sem。 我們事先編輯好數據文件1.dat和2.dat,假設它們的內容分別為1 2 3 4 5 6 7 8 9 10和 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 ,我們運行sem,得到如下的結果:

Multiply:-1*-2=2
Plus:-1+-2=-3
Multiply:9*10=90
Plus:-9+-10=-19
Multiply:-7*-8=56
Plus:-5+-6=-11
Multiply:-3*-4=12
Plus:9+10=19
Plus:7+8=15
Plus:5+6=11

  從中我們可以看出各個線程間的競爭關系。而數值并未按我們原先的順序顯示出來這是由于size這個數值被各個線程任意修改的緣故。這也往往是多線程編程要注意的問題。
posted on 2009-06-12 19:47 ivy-jie 閱讀(468) 評論(0)  編輯 收藏 引用 所屬分類: linux

只有注冊用戶登錄后才能發表評論。
網站導航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            亚洲欧美日韩国产综合在线| 免费成人高清| 国产精品成人久久久久| 欧美在线免费观看| 美国成人直播| 欧美一区二区视频97| 亚洲天堂网在线观看| 宅男噜噜噜66国产日韩在线观看| 亚洲国产日韩一级| 精品成人国产| 久久影院午夜论| 久久精品一本| 久久精品30| 欧美一区二区三区四区高清| 亚洲综合精品自拍| 欧美一区二区三区四区夜夜大片| 日韩网站免费观看| 欧美国产成人在线| 欧美激情成人在线| 亚洲电影免费观看高清完整版在线观看 | 国内一区二区三区| 国产欧美精品一区二区三区介绍| 国产精品极品美女粉嫩高清在线| 国产精品嫩草影院av蜜臀| 国产精品成人一区二区网站软件| 欧美区一区二区三区| 欧美日韩国产精品专区| 国产日韩亚洲欧美综合| 国产资源精品在线观看| 亚洲高清自拍| 亚洲午夜在线视频| 午夜精品一区二区在线观看| 久久久国际精品| 亚洲高清色综合| 艳妇臀荡乳欲伦亚洲一区| 欧美在线关看| 欧美日本在线视频| 国产精品视频久久一区| 亚洲日本成人| 午夜精品国产更新| 男人的天堂成人在线| 一区二区三区www| 欧美在线视频免费观看| 久久高清国产| 国产精品第一页第二页第三页| 国产一区欧美日韩| 一区二区国产日产| 亚洲一区日韩在线| 蜜臀av国产精品久久久久| 老牛嫩草一区二区三区日本| 日韩特黄影片| 能在线观看的日韩av| 一区在线影院| 亚洲欧美精品在线观看| 欧美成人国产| 久久精品国产99精品国产亚洲性色 | 亚洲第一黄色网| 亚洲视频综合| 欧美日韩性生活视频| 在线成人激情视频| 午夜精品久久久久久久99黑人| 欧美中文在线免费| 国产乱码精品一区二区三区忘忧草 | 亚洲国产精品嫩草影院| 欧美精品一区二区精品网| 久久久午夜电影| 亚洲国产精品福利| 亚洲小视频在线观看| 亚洲人成人77777线观看| 久久成年人视频| 国产精品久久久久久久久久直播 | 午夜精品久久久久影视 | 91久久精品一区| 欧美大片免费观看| 日韩亚洲一区在线播放| 亚洲欧美精品一区| 一区二区三区**美女毛片| 欧美精品一区二区三区在线看午夜| 国产一区二区欧美| 久久久久九九视频| 亚洲精品精选| 国产精品卡一卡二| 亚洲午夜日本在线观看| 99re热精品| 国产日韩在线不卡| 久久精品国产一区二区三区免费看| 一区二区三区四区五区精品| 国产精品videosex极品| 亚洲欧美国产高清va在线播| 亚洲午夜日本在线观看| 国产亚洲第一区| 91久久综合| 欧美性事免费在线观看| 亚洲欧美成人一区二区三区| 欧美影院午夜播放| 亚洲高清不卡在线观看| 免费在线视频一区| 欧美中文字幕在线视频| 亚洲精品一二三区| 亚洲精品日产精品乱码不卡| 国产精品国产成人国产三级| 久久久久久久久久久一区| 久久影视精品| 亚洲一区二区三区乱码aⅴ| 久久婷婷激情| 国产精品99久久99久久久二8 | 亚洲午夜激情| 亚洲激情欧美| 国产婷婷色综合av蜜臀av| 久久亚洲午夜电影| 欧美精品国产一区| 久久蜜桃资源一区二区老牛 | 香蕉免费一区二区三区在线观看 | 亚洲美女少妇无套啪啪呻吟| 国产精品一级| 亚洲电影专区| 国产一区二区三区四区三区四 | 黄色成人av在线| 欧美成人免费大片| 欧美亚洲成人精品| 亚洲精品小视频在线观看| 国产视频一区在线观看| 欧美激情亚洲国产| 一区三区视频| 久久婷婷蜜乳一本欲蜜臀| 久久国产精品第一页| 欧美视频一区二区| 亚洲国产天堂久久综合| 一色屋精品视频在线观看网站 | 欧美精品一区二区三区在线看午夜| 老司机久久99久久精品播放免费| 国产精品一区二区久久久| 一区二区三区欧美| 一区二区三区日韩| 欧美破处大片在线视频| 亚洲国产日韩欧美在线99| 亚洲国产精品ⅴa在线观看| 久久尤物视频| 亚洲国产mv| 亚洲欧洲一区二区在线播放| 久久av一区二区| 久久久亚洲综合| 激情综合激情| 久久久噜噜噜久噜久久 | 亚洲一区二区三区成人在线视频精品 | 亚洲人成高清| 欧美欧美天天天天操| 亚洲人精品午夜| 亚洲欧美乱综合| 国产一区二区av| 久久蜜桃av一区精品变态类天堂| 久久综合九色综合久99| 亚洲国产一区二区a毛片| 欧美国产高潮xxxx1819| 亚洲伊人久久综合| 欧美激情一区二区| 亚洲免费观看高清完整版在线观看| 蜜臀a∨国产成人精品| 亚洲国产成人av好男人在线观看| 日韩午夜av电影| 欧美日韩亚洲一区二区三区四区| 亚洲伦理精品| 欧美一区免费视频| 在线播放国产一区中文字幕剧情欧美| 欧美一区二区精品在线| 美女视频黄 久久| 99精品欧美一区二区三区| 国产麻豆综合| 欧美va亚洲va国产综合| 一区二区三区日韩欧美精品| 久久久999成人| 亚洲日韩成人| 国产视频久久网| 欧美三级第一页| 久久一二三四| 亚洲婷婷综合久久一本伊一区| 麻豆精品国产91久久久久久| 中文精品视频一区二区在线观看| 国产在线不卡精品| 欧美激情亚洲国产| 欧美一区二区在线免费播放| 亚洲国产精品精华液网站| 香蕉亚洲视频| 99国内精品久久| 国外成人在线| 国产精品卡一卡二| 欧美精品日本| 久久久久久久综合| 亚洲综合色在线| 日韩视频免费观看高清在线视频| 久久亚洲精品伦理| 亚洲色图自拍| 亚洲精品乱码久久久久久蜜桃91 | 久久久精品日韩欧美| 99在线精品视频在线观看| 精品动漫av| 国产一区自拍视频| 国产美女精品视频免费观看| 欧美日韩精品免费观看视一区二区| 老牛嫩草一区二区三区日本|