《嵌入式linux應用程序開發標准教程》筆記——9.多線程編程


 

  線程是輕量級進程,創建線程的開銷要比進程小得多,在大型程序中應用廣泛。

9.1 線程概述

  • 進程包含自己的代碼、數據、堆棧、資源等等,創建和切換的開銷比較大;
  • 線程是輕量級的進程,調度的最小單元,同一個進程內的線程可以共享資源;
  • 線程的上下文開銷比進程小得多;
  • 線程有自己的堆棧,但是用戶空間共享,例如一個線程修改全局變量,會影響到同一個進程內的另一個線程;

  • linux里其實線程就是輕量級的進程,都用PCB表示,只不過新建線程時,共享同一個進程內的資源。線程應該是單獨調度

9.2 線程編程

  線程一般在用戶空間操作,pthread線程庫是通用的POXIS標准。

9.2.1 函數說明及示例

  •  創建線程:pthread_create(),  確定線程的入口函數,創建后就開始執行;
  •  退出線程:方式1,入口函數執行完后以后,自動結束;方式2,主動退出,pthead_exit().
  •  釋放資源:pthread_join(),類似wait(),也是阻塞的
  •  終止別的進程:pthread_cancel(),終止別的線程;pthread_setcancel()/pthread_setcanceltype()設置是否允許別的線程結束自己及結束的方式等

【注意】:

  • 線程如果調用exit(),會使整個進程退出。

 

#include <pthread.h>

int pthread_create( pthread_t * thread, pthread_attr_t * attr, void *(*start_routine)(void *), void * arg );
參數:
  thread,線程標識符
  attr,線程屬性
  start_routine, 線程入口函數
  arg,傳遞給入口函數的參數
返回值:
  成功:0
  出錯:返回錯誤碼

void pthread_exit( void * retval );
參數:
  retval,線程結束時的返回值,由其他線程使用pthread_join()獲取
  【注意】:如果返回變量,不用使用線程的局部變量,因為推出以后,線程的堆棧空間可能被占用,導致返回值的變化。 可以返回絕對值或者變量值。

int pthread_join( pthread_t thread, void ** thread_return );
參數:
  thread,要等待的線程標識符
  thread_return, 被等待線程的返回值,實質是把 *thread_return = retval,其中retval是pthread_exit的指針,即傳遞了一個地址而已。可以利用傳遞的地址直接傳遞返回值,也可以利用變量傳遞,詳見后面用例。
返回值:
  成功:0
  出錯:返回錯誤碼

int pthread_cancel( pthread_t thread )
參數:
  thread,要取消的線程標識符
返回值:
  成功:0
  出錯:返回錯誤碼

 

//#define EXIT_USE_VER // 用變量返回,不可用線程自己的局部變量,因為退出以后有可能被復用

#define EXIT_USE_POINT // 用指針的賦值返回,適用於返回絕對值

#include <stdio.h> // printf
#include <stdlib.h> // exit
#include <unistd.h>
#include <sys/types.h> // pid_t
#include <fcntl.h>
#include <pthread.h>

#define MAX_DELAY_SECONDS 10.0
int rtn[3];

void fun_thread( int thread_no )
{
  unsigned char count=0;
  int delay_time=0;

  printf("thread %d start.\r\n",thread_no);
  for(count=0;count<3;count++)
  {
    delay_time = (int)(rand() * MAX_DELAY_SECONDS/RAND_MAX)+1;
    sleep(delay_time);
    printf("thread %d loop %dst delay second %d\r\n",thread_no,count,delay_time);
  }
#ifdef EXIT_USE_VER
  rtn[thread_no] = thread_no;
  pthread_exit((void*)&rtn[thread_no]); // 不同線程退出值不同,僅測試用
#endif
#ifdef EXIT_USE_POINT
  pthread_exit((void*)0xaa); // 可以返回絕對值
#endif
}

int main(int args, char *argv[])
{
  unsigned thread_no;
  pthread_t thread[3];
#ifdef EXIT_USE_VER
  int * thread_rtn;
#endif
#ifdef EXIT_USE_POINT
  void * thread_rtn;
#endif

  printf("start.\r\n");
  for( thread_no=0; thread_no<3; thread_no++ )
  {
    if( pthread_create(&thread[thread_no],NULL,fun_thread,(void*)thread_no) <0 )
    printf("Create thread %d err.\r\n",thread_no);
  }

  // exit(0); 不能有此函數,否則創建完線程以后,exit會把本進程及內部所有線程一並退出

  // 方法1:等待輸入后結束
  // getchar();

  // 方法2:用pthread_join()等待所有線程結束
  for( thread_no=0; thread_no<3; thread_no++ )
  {
#ifdef EXIT_USE_VER
    if( pthread_join(thread[thread_no], (void**)&thread_rtn) < 0 )
#endif
#ifdef EXIT_USE_POINT
    if( pthread_join(thread[thread_no], &thread_rtn) < 0 )
#endif
      printf( "pthread_join:thread %d err.\r\n", thread_no);
    else
#ifdef EXIT_USE_VER
      printf( "pthread_join:thread %d status %d.\r\n", thread_no,*thread_rtn);
#endif
#ifdef EXIT_USE_POINT
      printf( "pthread_join:thread %d status %d.\r\n", thread_no,(int)thread_rtn);
#endif
  }

  exit(0);
}

$ ./example
start.
thread 2 start.
thread 1 start.
thread 0 start.
thread 1 loop 0st delay second 4
thread 0 loop 0st delay second 8
thread 2 loop 0st delay second 9
thread 2 loop 1st delay second 2
thread 1 loop 1st delay second 8
thread 2 loop 2st delay second 4
thread 0 loop 1st delay second 10
thread 1 loop 2st delay second 8
thread 0 loop 2st delay second 3
pthread_join:thread 0 status 170.
pthread_join:thread 1 status 170.
pthread_join:thread 2 status 170.

 

9.2.2 線程之間的同步和互斥

  互斥鎖適用於只有1個共享資源;

  信號量適用多個共享資源的同步。

9.2.2.1 互斥鎖線程控制

  •  互斥鎖可簡單理解成全局變量,只有上鎖和解鎖兩種狀態;
  •  若干其他線程希望上鎖一個已經被上鎖的互斥鎖,則該線程掛起
  •  相關函數:
    • pthread_mutex_init(): 初始化
    • pthread_mutex_lock():上鎖
    • pthread_mutex_trylock():判斷上鎖
    • pthread_mutex_unlock():解鎖
    • pthread_mutex_destroy():清除
#include <pthread.h>

int pthread_mutex_init( pthread_mutex_t * mutex, const pthread_mutexattr_t * mutexattr );
參數:
  mutex,互斥鎖;
  mutexattr,互斥鎖種類,若為NULL,則使用默認的互斥鎖類型。具體的鎖類型后續研究吧
返回值:
  成功:0
  出錯:返回錯誤碼

int pthread_mutex_lock( pthread_mutex_t * mutex );
int phtread_mutex_trylock( pthread_mutex_t * mutex );
int pthread_mutex_unlock( pthread_mutex_t * mutex );
int pthread_mutex_unlock( pthread_mutex_t * mutex );
參數:
  mutex,互斥鎖
返回值:
  成功:0
  出錯:-1

 

增加互斥鎖,使原本獨立的無序程序按設計預期運行

/* 9-1,pthread_mutex */

//#define EXIT_USE_VER // 用變量返回,不可用線程自己的局部變量,因為退出以后有可能被復用
#define EXIT_USE_POINT // 用指針的賦值返回,適用於返回絕對值

#include <stdio.h> // printf
#include <stdlib.h> // exit
#include <unistd.h>
#include <sys/types.h> // pid_t
#include <fcntl.h>
#include <pthread.h>

#define MAX_DELAY_SECONDS 10.0

#ifdef EXIT_USE_VER
int rtn[3];
#endif

pthread_mutex_t g_mutex;

void fun_thread( int thread_no )
{
  unsigned char count=0;
  int delay_time=0;

  pthread_mutex_lock(&g_mutex);
  printf("thread %d start.\r\n",thread_no);
  for(count=0;count<3;count++)
  {
    delay_time = (int)(rand() * MAX_DELAY_SECONDS/RAND_MAX)+1;
    sleep(delay_time);
    printf("thread %d loop %dst delay second %d\r\n",thread_no,count,delay_time);
  }
  pthread_mutex_unlock(&g_mutex);
#ifdef EXIT_USE_VER
  rtn[thread_no] = thread_no;
  pthread_exit((void*)&rtn[thread_no]); // 不同線程退出值不同,僅測試用
#endif
#ifdef EXIT_USE_POINT
  pthread_exit((void*)0); // 可以返回絕對值
#endif
}

int main(int args, char *argv[])
{
  unsigned thread_no;
  pthread_t thread[3];
#ifdef EXIT_USE_VER
  int * thread_rtn;
#endif
#ifdef EXIT_USE_POINT
  void * thread_rtn;
#endif

  pthread_mutex_init(&g_mutex,NULL);

  printf("start.\r\n");
  for( thread_no=0; thread_no<3; thread_no++ )
  {
    if( pthread_create(&thread[thread_no],NULL,fun_thread,(void*)thread_no) <0 )
    printf("Create thread %d err.\r\n",thread_no);
  }

  // exit(0); 不能有此函數,否則創建完線程以后,exit會把本進程及內部所有線程一並退出

  // 方法1:等待輸入后結束
  // getchar();

  // 方法2:用pthread_join()等待所有線程結束
  for( thread_no=0; thread_no<3; thread_no++ )
  {
#ifdef EXIT_USE_VER
    if( pthread_join(thread[thread_no], (void**)&thread_rtn) < 0 )
#endif
#ifdef EXIT_USE_POINT
    if( pthread_join(thread[thread_no], &thread_rtn) < 0 )
#endif
      printf( "pthread_join:thread %d err.\r\n", thread_no);
    else
#ifdef EXIT_USE_VER
      printf( "pthread_join:thread %d status %d.\r\n", thread_no,*thread_rtn);
#endif
#ifdef EXIT_USE_POINT
      printf( "pthread_join:thread %d status %d.\r\n", thread_no,(int)thread_rtn);
#endif

  }

  pthread_mutex_destroy(&g_mutex);
  exit(0);
}

 

$ ./example
start.
thread 2 start.
thread 2 loop 0st delay second 9
thread 2 loop 1st delay second 4
thread 2 loop 2st delay second 8
thread 1 start.
thread 1 loop 0st delay second 8
thread 1 loop 1st delay second 10
thread 1 loop 2st delay second 2
thread 0 start.
thread 0 loop 0st delay second 4
thread 0 loop 1st delay second 8
thread 0 loop 2st delay second 3
pthread_join:thread 0 status 0.
pthread_join:thread 1 status 0.
pthread_join:thread 2 status 0.

 

9.2.2.2 信號量線程控制

  •  sem,本質上是非負的整數計數器
  •  P:sem-1
  •  V: sem+1
  • sem>=0,進程/線程具有公共資源的訪問權
  • sem<0,進程/線程沒有公共資源訪問權,阻塞,指導sem>=0為止
  • 用途:同步和互斥,若互斥,往往只有1個sem;同步時,往往有多個sem,安排不同初值來實現順序執行
  • 線程和進程的sem接口函數不同

         互斥流程

 

 

#include <semaphore.h>

int sem_init( sem_t *sem, int pshared, unsigned int value );  // 創建信號量,並初始化它的值
參數:
  sem,信號量指針
  pshared,決定信號量能在幾個進程間共享,linux不支持,只能用0;
  value,信號量初始值
返回值:
  成功:0
  出錯:-1

int sem_wait( sem_t *sem );    // P操作,阻塞
int sem_trywait( sem_t *sem );  // P操作,不阻塞
int sem_post( sem_t *sem );    // V操作
int sem_getvalue(set_t * sem ); // 得到信號量的值
int sem_destroy(sem_t * sem );  // 刪除信號量
參數:
  sem,信號量指針
返回值:
  成功:0
  出錯:-1

 

 用信號量實現3個進程的有序執行,倒着來,2->1->0

/* 9-3,sem */

#include <stdio.h> // printf
#include <stdlib.h> // exit
#include <unistd.h>
#include <sys/types.h> // pid_t
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>

#define MAX_DELAY_SECONDS 2.0

sem_t g_sem[3];

void fun_thread( int thread_no )
{
  unsigned char count=0;
  int delay_time=0;

  sem_wait(&g_sem[thread_no]);

  printf("thread %d start.\r\n",thread_no);
  for(count=0;count<3;count++)
  {
    delay_time = (int)(rand() * MAX_DELAY_SECONDS/RAND_MAX)+1;
    sleep(delay_time);
    printf("thread %d loop %dst delay second %d\r\n",thread_no,count,delay_time);
  }

  if( thread_no!=0 )
    sem_post(&g_sem[thread_no-1]);
  pthread_exit((void*)0); // 可以返回絕對值
}

int main(int args, char *argv[])
{
  unsigned thread_no;
  pthread_t thread[3];
  void * thread_rtn;

  printf("start.\r\n");

  sem_init(&g_sem[0], 0,0);
  sem_init(&g_sem[1], 0,0);
  sem_init(&g_sem[2], 0,1);  // 允許2先運行


  for( thread_no=0; thread_no<3; thread_no++ )
  {
    if( pthread_create(&thread[thread_no],NULL,fun_thread,(void*)thread_no) <0 )
    printf("Create thread %d err.\r\n",thread_no);
  }

  // exit(0); 不能有此函數,否則創建完線程以后,exit會把本進程及內部所有線程一並退出

  // 方法1:等待輸入后結束
  // getchar();

  // 方法2:用pthread_join()等待所有線程結束
  for( thread_no=0; thread_no<3; thread_no++ )
  {
  if( pthread_join(thread[thread_no], &thread_rtn) < 0 )
    printf( "pthread_join:thread %d err.\r\n", thread_no);
  else
    printf( "pthread_join:thread %d status %d.\r\n", thread_no,(int)thread_rtn);
  }
  sem_destroy(&g_sem[0]);
  sem_destroy(&g_sem[1]);
  sem_destroy(&g_sem[2]);

  exit(0);
}

$ ./example
start.
thread 2 start.
thread 2 loop 0st delay second 2
thread 2 loop 1st delay second 1
thread 2 loop 2st delay second 2
thread 1 start.
thread 1 loop 0st delay second 2
thread 1 loop 1st delay second 2
thread 1 loop 2st delay second 1
thread 0 start.
thread 0 loop 0st delay second 1
thread 0 loop 1st delay second 2
thread 0 loop 2st delay second 1
pthread_join:thread 0 status 0.
pthread_join:thread 1 status 0.
pthread_join:thread 2 status 0.

 

 

9.2.3 線程屬性

  • pthread_create()的第二個參數可以設定線程屬性,包括綁定、分離、堆棧、優先級等;
  • 綁定屬性,內核線程與用戶線程一對一,linux用內核線程調度,如果綁定,能夠保證需要時總能有對應的內核線程;如果不綁定,需要時系統分配內核線程;比較重要的線程倒是可以考慮設置綁定屬性
  • 分離屬性,默認非分離,線程結束也留有一定的資源,pthread_join以后才徹底釋放;分離,執行結束馬上釋放,有問題,例如執行很快,可能在create之前線程就結束返回了,此時連線程號都釋放了,可能導致出錯。完全沒必要設置分離屬性。
#include <pthread.h>

int pthread_attr_init( pthread_attr_t * attr );    // 初始化
參數:
  attr,線程屬性
返回值:
  成功:0
  出錯:返回錯誤碼

int pthread_attr_setscope( pthread_attr_t * attr, int scope );  // 是否綁定
參數:
  attr,線程屬性
  scope,PTHREAD_SCOPE_SYSTEM:綁定
     PTHREAD_SCOPE_PROCESS:非綁定
返回值:
  成功:0
  出錯:-1
int pthread_attr_setdetachstate( pthread_attr_t * attr, int detachstate );  // 是否分離
參數:
  attr,線程屬性
  scope,PTHREAD_CREATE_DETACHED:分離
     PTHREAD_SCOPE_JOINABLE:非分離
返回值:
  成功:0
  出錯:-1

int pthread_attr_getschedparam( pthread_attr_t * attr, struct sched_param * param );  // 是否分離
參數:
  attr,線程屬性
  param,線程優先級
返回值:
  成功:0
  出錯:-1
int pthread_attr_setschedparam( pthread_attr_t * attr, struct sched_param * param );  // 是否分離
參數:
  attr,線程屬性
  param,線程優先級
返回值:
  成功:0
  出錯:-1

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM