linux多線程編程

      線程的概念就不贅述了,下面先講講線程和進程相比具有那些優點或者缺點:

      1.線程能夠使一個程序看起來在同一時間幹了兩件或者多件事,這樣的功能有時候是非常有用的。

      2.線程適用與一個具有幾部分相對獨立的工作的進程,這樣在一個線程阻塞的時候另一個線程的運行不會被中斷。

      3.由於多核CPU的廣泛應用,將一個進程分解爲多個線程可以更有效的利用硬件資源。

      4.多線程比多進程需要更少的資源,同時更具實用性。

      5.多線程程序的設計需要非常小心,一些變量共享或者同步很可能會發生錯誤。

      6.調試多線程程序將會非常複雜。

創建新的線程

      在pthread.h裏定義了與線程編程相關的一些函數。函數pthread_create創建一個新的線程,與fork函數創建進程類似。函數的定義如下:

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

      第一個參數標識了一個新的線程,第二個參數指定了線程的屬性,一般情況下可置爲NULL,後兩個參數標識了新的線程所要執行的函數的名稱以及傳入這個函數的參數。
終止一個線程
      void pthread_exit(void *retval);
等待一個線程
      int pthread_join(pthread_t th, void **thread_return);
      相當於進程函數裏的wait函數。
      下面是多線程使用的一個簡單示例,首先程序創建了一個新的線程,並將新線程的結果返回給原始線程。
      在編譯之前,首先要確認_REENTRANT已經定義了,少數的系統裏可能也需要定義_POSIX_C_SOURCE;其次要確認函數庫是不是較新的,否則可能無法編譯通過。
信號量
      關於信號量的概念可以百度或者谷歌。linux下有兩種信號量,一種是POSIX的供線程使用的信號量,一種是針對進程的系統V信號量。
創建信號量
      int sem_init(sem_t *sem, int pshared, unsigned int value);
      該函數初始化一個由sem指向的信號量。pshared指定該信號量的共享選項,如果該值爲0,則表明該信號量只能由當前進程使用,若不爲0,則可以被多個進程使用。一般在linux下,該值爲0。value指定該信號量的初始值。
控制信號量的值
      int sem_wait(sem_t * sem);
      int sem_post(sem_t * sem);
      這兩個函數的參數都是一個指向信號量的指針。sem_post函數將信號量的值加1。而sem_wait函數在信號量不爲0的情況下將信號量減1。若在信號量爲0的時候調用該函數,則將使線程阻塞直到信號量的值不爲0。如果有兩個線程同時在等待一個信號量,那麼當信號量的值非0時,將只有一個線程能夠繼續運行,而另一個線程將繼續等待。
銷燬信號量
      int sem_destroy(sem_t * sem);
      當信號量使用完畢時,調用該函數收回由信號量使用的所有資源。
下面的代碼演示了信號量的使用:
 
同步互斥
      另一種可以讓多線程程序達到同步的方法是使用互斥。當希望只有一個線程能訪問某個對象時使用互斥,該方法可以鎖住某個對象或對某個對象進行解鎖。用來進行互斥的函數與信號量的函數在描述以及功能上有些類似。linux提供的關於互斥的函數主要如下:
      int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t*mutexattr);
      int pthread_mutex_lock(pthread_mutex_t *mutex));
      int pthread_mutex_unlock(pthread_mutex_t *mutex);
      int pthread_mutex_destroy(pthread_mutex_t *mutex);
下面的代碼演示了互斥的使用,當中核心變量在使用互斥鎖上之後,只能由一個線程訪問。而且一些函數返回值的檢查被省略了。
 
線程屬性
      線程的屬性中能夠被設定的不多,而在能設定的當中,我們將只討論那些可能會被用到的,其它的函數可以查閱一些幫助文檔。在設定線程屬性的函數中最重要的函數是:
      int pthread_attr_init(pthread_attr_t *attr);
      該函數初始化一個線程屬性對象。在調用完該函數之後可以對線程的一些屬性進行更改,主要包含以下一些函數:
      int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
      int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
      int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);
      int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy);
      int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param*param);
      int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param*param);
      int pthread_attr_setinheritsched(pthread_attr_t *attr, int inherit);
      int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inherit);
      int pthread_attr_setscope(pthread_attr_t *attr, int scope);
      int pthread_attr_getscope(const pthread_attr_t *attr, int *scope);
      int pthread_attr_setstacksize(pthread_attr_t *attr, int scope);
      int pthread_attr_getstacksize(const pthread_attr_t *attr, int *scope);
      這些函數主要對以下屬性進行讀寫:
      1.detachedstate 該屬性能夠消除線程進行應答的必要,兩個可能的值爲PTHREAD_CREATE_JOINABLE和PTHREAD_CREATE_DETACHED。默認的值爲PTHREAD_CREATE_JOINABLE,此時線程之間允許進行響應,當該屬性的值爲PTHREAD_CREATE_DETACHED時,函數pthread_join將不能調用。
      2.schedpolicy 該屬性用來對線程進行定位。有三個選項:SCHED_OTHER,SCHED_RP和SCHED_FIFO。默認情況下的值爲SCHED_OTHER。
      3.schedparam 該屬性與schedpolicy搭配使用。
      4.inheritsched 該屬性有兩個可能的值:PTHREAD_EXPLICIT_SCHED和PTHREAD_INHERIT_SCHED。前者表示該線程的調度完全由自己的屬性決定,而後者則表示該線程的調度由創建它的線程的屬性決定。
      5.scope 只有一個值PTHREAD_SCOPE_SYSTEM,該屬性決定如何對線程進行調度。
      6.stacksize 該屬性決定線程的棧的大小。
   
      下面的代碼演示了對線程的屬性的更改,程序首先新建了一個屬性對象,然後將它賦予給一個新創建的線程。
 
      可能的運行結果如下:
$ ./thread5
Waiting for thread to say it’s finished...
thread_function is running. Argument was Hello World
Waiting for thread to say it’s finished...
Waiting for thread to say it’s finished...
Waiting for thread to say it’s finished...
Second thread setting finished flag, and exiting now
Other thread finished, bye!
      關於線程屬性的進一步探討可參閱有關文獻,在此不再贅述。
取消一個線程
      有時候可能需要在一個線程裏讓另一個線程終結,此時可以使用如下函數:
      int pthread_cancel(pthread_t thread);
      同時一個線程還能夠通過函數pthread_setcancelstate設置它的“取消狀態”。  其定義如下:
      int pthread_setcancelstate(int state, int *oldstate);
      有兩個值,PTHREAD_CANCEL_ENABLE允許線程接收其它線程的終結要求,PTHREAD_CANCEL_DISABLE則拒絕其它線程的該要求。如果接受了其它線程的終結要求,還有第二級選項影響線程的狀態,使用函數pthread_setcanceltype進行設定,其定義如下:
      int pthread_setcanceltype(int type, int *oldtype);
      如果type的值爲PTHREAD_CANCEL_ASYNCHRONOUS,則終結要求將被立即執行。若值爲PTHREAD_CANCEL_DEFERRED,則終結要求要等線程的以下函數之一執行才能被執行: pthread_join, pthread_cond_wait,pthread_cond_timedwait, pthread_testcancel, sem_wait, 或者sigwait。
下面是一個多線程的例子:
 
參考文獻:《Begining Linux Programming 4th edition》-------Neil Matthew
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章