1. 程式人生 > >Pthread執行緒基礎學習

Pthread執行緒基礎學習

後面會嘗試使用冰搜和goole搜尋來學習技術,網際網路上知識的學習也是符合二八定律的,既然如此,我們何不去選擇最好的文章呢。
文章參考:
https://randu.org/tutorials/threads/
http://www.yolinux.com/TUTORIALS/LinuxTutorialPosixThreads.html
https://blog.csdn.net/stpeace/article/details/79575493

一、多執行緒相關API學習

執行緒的學習主要包括三方面:執行緒管理(建立、分離、joinable以及設定執行緒屬性等);互斥鎖(建立、銷燬、lock 和unlock等);條件變數(conditon variable)。
這裡記錄一些重要的API作為記錄學習吧。

執行緒建立
int pthread_create(pthread_t *thread, pthread_attr_t *attr, 
                   void *(*start_routine)(void *), void *arg);

引數:

pthread_t *thread: the actual thread object that contains pthread id
pthread_attr_t *attr: attributes to apply to this thread
void *(*start_routine)(void *)
: the function this thread executes void *arg: arguments to pass to thread function above

執行緒建立時應保證執行緒ID thread被成功建立(檢查返回值);執行緒屬性attr預設值為NULL,可設定為其他屬性;start_routine是建立執行緒後所執行的函式體;arg是傳入的引數。

執行緒等待和終止
void pthread_exit(void *value_ptr);
int pthread_join(pthread_t thread, void **value_ptr);

pthread_exit()

終止執行緒,並且提供指標變數*value_ptrpthread_join()呼叫
pthread_join() 阻塞呼叫執行緒呼叫,並等待執行緒結束,得到可選返回值*value_ptr

//1. 確保檢查重要函式返回值
//2. pthread_create()的第二個引數為NULL,屬性為預設屬性(比如joinable)
//3. 引數傳遞
//4. pthread_join()是阻塞的,可接收pthread_exit()返回的執行緒結果資訊
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define NUM_THREADS  2

//thread argument struct
typedef struct _thread_data {
	int tid;
	double studff;
} thread_data;

//thread function
void *thr_func(void *arg) {
	thread_data *data = (thread_data *)arg;

	printf("from thr_func, thread id:%d\n",data->tid);

	pthread_exit(NULL);
}

int main() {
	pthread_t thr[NUM_THREADS];
	int i,rc;

	//thread_data argument array
	thread_data thr_data[NUM_THREADS];

	//create threads
	for(i=0; i<NUM_THREADS; ++i) {
		thr_data[i].tid = i;
		if((rc = pthread_create(&thr[i],NULL,thr_func,&thr_data[i]))) {
			fprintf(stderr,"error:pthread_create,rc: %d\n",rc);
			return EXIT_FAILURE;
		}
	}

	//block untill all threads complete
	
	for(i=0; i<NUM_THREADS; ++i) {
		pthread_join(thr[i],NULL);
	}
	
	return 0;
}
執行緒屬性

屬性的初始化、屬性的設定set、屬性的獲取get

int pthread_attr_init(pthread_attr_t *attr);
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize);
int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched);
int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param);
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);
int pthread_attr_setscope(pthread_attr_t *attr, int contentionscope);
int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr);
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);

示例:

#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define NUM_THREADS  2

//thread argument struct
typedef struct _thread_data {
	int tid;
	double studff;
} thread_data;

//thread function
void *thr_func(void *arg) {
	thread_data *data = (thread_data *)arg;

	printf("from thr_func, thread id:%d\n",data->tid);

	pthread_exit(NULL);
}

int main() {
	pthread_t thr[NUM_THREADS];
	int i,rc;

	//thread_data argument array
	thread_data thr_data[NUM_THREADS];

	//initialize and set thread datached
	pthread_attr_t attr;
	pthread_attr_init(&attr);
	pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
	//create threads
	for(i=0; i<NUM_THREADS; ++i) {
		thr_data[i].tid = i;
		if((rc = pthread_create(&thr[i],&attr,thr_func,&thr_data[i]))) {
			fprintf(stderr,"error:pthread_create,rc: %d\n",rc);
			return EXIT_FAILURE;
		}
	}

	pthread_attr_destroy(&attr);
	sleep(5);

	return 0;
}

其實,這個程式碼和上一個程式碼是類似的:
執行緒一得等到pthread_join來釋放系統資源,執行緒一的執行緒函式一結束就自動釋放資源
總之為了在使用 pthread 時避免執行緒的資源線上程結束時不能得到正確釋放,從而避免產生潛在的記憶體洩漏問題,在對待執行緒結束時,要確保該執行緒處於 detached 狀態,否著就需要呼叫 pthread_join() 函式來對其進行資源回收。

執行緒互斥鎖
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);//動態初始化
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; //靜態初始化

//acquire a lock on the specified mutex variable. If the mutex is already locked by another thread, 
//this call will block the calling thread until the mutex is unlocked.
int pthread_mutex_lock(pthread_mutex_t *mutex);
// attempt to lock a mutex or will return error code if busy. Useful for preventing deadlock conditions.
int pthread_mutex_trylock(pthread_mutex_t *mutex);
//unlock a mutex variable. An error is returned if mutex is already unlocked or owned by another thread.
int pthread_mutex_unlock(pthread_mutex_t *mutex);

示例

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

void * updateCounter(void*);

pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
int counter =0;

int main(){

  int rc1,rc2;
  pthread_t thread1,thread2;
    if( (rc1=pthread_create( &thread1, NULL, &updateCounter, NULL)) )
    {
       printf("Thread creation failed: %d\n", rc1);
    }
    if( (rc2=pthread_create( &thread2, NULL, &updateCounter, NULL)) )
     {
         printf("Thread creation failed: %d\n", rc2);
     }
    /* Wait till threads are complete before main continues.*/
    pthread_join( thread1, NULL);
    pthread_join( thread2, NULL);

  	return 0;

}

void * updateCounter(void*){
   pthread_mutex_lock( &mutex1 );
   counter++;
   printf("Counter value: %d\n",counter);
   pthread_mutex_unlock( &mutex1 );
}
執行緒條件變數
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);

示例:

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

pthread_mutex_t count_mutex     = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t  condition_var   = PTHREAD_COND_INITIALIZER;

void *functionCount1(void*);
void *functionCount2(void*);
int  count = 0;
#define COUNT_DONE  10
#define COUNT_HALT1  3
#define COUNT_HALT2  6

int main()
{
   pthread_t thread1, thread2;
   pthread_create( &thread1, NULL, &functionCount1, NULL);
   pthread_create( &thread2, NULL, &functionCount2, NULL);

   pthread_join( thread1, NULL);
   pthread_join( thread2, NULL);

   printf("Final count: %d\n",count);
   exit(EXIT_SUCCESS);
}

// Write numbers 1-3 and 8-10 as permitted by functionCount2()
void *functionCount1(void*)
{
   for(;;)
   {
      // Lock mutex and then wait for signal to relase mutex
      pthread_mutex_lock( &count_mutex );

      // Wait while functionCount2() operates on count
      // mutex unlocked if condition varialbe in functionCount2() signaled.
      pthread_cond_wait( &condition_var, &count_mutex );
      count++;
      printf("Counter value functionCount1: %d\n",count);

      pthread_mutex_unlock( &count_mutex );
      if(count >= COUNT_DONE) return(NULL);
    }
}

// Write numbers 4-7
void *functionCount2(void*)
{
    for(;;)
    {
       pthread_mutex_lock( &count_mutex );

       if( count < COUNT_HALT1 || count > COUNT_HALT2 )
       {
          // Condition of if statement has been met. 
          // Signal to free waiting thread by freeing the mutex.
          // Note: functionCount1() is now permitted to modify "count".
          pthread_cond_signal( &condition_var );
       }
       else
       {
          count++;
          printf("Counter value functionCount2: %d\n",count);
       }

       pthread_mutex_unlock( &count_mutex );
       if(count >= COUNT_DONE) return(NULL);
    }

}

在這裡插入圖片描述
標準模板以及解釋:

void *thr_func1(void *arg) {
  /* thread code blocks here until MAX_COUNT is reached */
  pthread_mutex_lock(&count_lock);
    while (count < MAX_COUNT) {
      pthread_cond_wait(&count_cond, &count_lock);
    }
  pthread_mutex_unlock(&count_lock);
  /* proceed with thread execution */
 
  pthread_exit(NULL);
}
 
/* some other thread code that signals a waiting thread that MAX_COUNT has been reached */
void *thr_func2(void *arg) {
  pthread_mutex_lock(&count_lock);
   
  /* some code here that does interesting stuff and modifies count */
 
  if (count == MAX_COUNT) {
    pthread_mutex_unlock(&count_lock);
    pthread_cond_signal(&count_cond);
  } else {
    pthread_mutex_unlock(&count_lock);
  }
 
  pthread_exit(NULL);
} 

為什麼這裡要記錄一些呢?pthread_cond_wait要使用while而不是if,解釋如下:

在這裡插入圖片描述

pthread Barrier及其它
int pthread_barrier_init(pthread_barrier_t *barrier, pthread_barrierattr_t *barrier_attr, unsigned int count);
 
pthread_barrier_t barrier = PTHREAD_BARRIER_INITIALIZER(count);
int pthread_barrier_wait(pthread_barrier_t *barrier);

pthread_kill() can be used to deliver signals to specific threads.
pthread_self() returns a handle on the calling thread.
pthread_equal() compares for equality between two pthread ids
pthread_once() can be used to ensure that an initializing function within a thread is only run once.

二、Pthread建立執行緒後必須使用join或detach釋放執行緒資源

When a joinable thread terminates, its memory resources (thread descriptor and stack) are
not deallocated until another thread performs pthread_join on it.
Therefore, pthread_join must be called once for each joinable thread created to avoid memory leaks.

有問題的程式碼:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
 
void* threadFunc(void* p)  
{  
	char szTest[1024 * 32] = {0};
    return NULL;  
}  
 
int main(void)
{
	pthread_t id;  
    pthread_create (&id, NULL, threadFunc, NULL);  
	
	sleep(1);
	printf("\n— main End —- \n");
    return 0;
}

在這裡插入圖片描述
解決方法有三個:

  1. 執行緒裡面呼叫 pthread_detach(pthread_self()) 這個方法最簡單
  2. 在建立執行緒的設定PTHREAD_CREATE_DETACHED屬性
  3. 建立執行緒後用 pthread_join() 一直等待子執行緒結束。

方法一:

void* threadFunc(void* p)  
{   //注意1:
	pthread_detach(pthread_self());
	char szTest[1024 * 32] = {0};
    return NULL;  
}  
int main(void)
{
	pthread_t id;  
	//注意2:引數2為NULL
    pthread_create (&id, NULL, threadFunc, NULL);  
	
	sleep(1);
	printf("\n— main End —- \n");
    return 0;
}

方法二:

void* threadFunc(void* p)  
{  
	char szTest[1024 * 32] = {0};
    return NULL;  
}  
 
int main(void)
{   //注意1:
	pthread_attr_t attr;	
	pthread_attr_init(&attr);
	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
 
	pthread_t id;  
	//注意2:引數2為attr
    pthread_create (&id, &attr, threadFunc, NULL);  


	
	sleep(1);
	printf("\n— main End —- \n");
    return 0;
}

方法三:

void* threadFunc(void* p)  
{  
	char szTest[1024 * 32] = {0};
    return NULL;  
}  
 
int main(void)
{
 
	pthread_t id;  
	//注意1:引數2為NULL
    pthread_create (&id, NULL, threadFunc, NULL);  
	//注意2:block
	pthread_join(id, NULL);
	
	sleep(1);
	printf("\n— main End —- \n");
    return 0;
}

再次檢測:
在這裡插入圖片描述

綜上,無論是pthread執行緒庫,還是最近我在使用C++11執行緒,對於joinable執行緒,如果最後沒有新增t.join(),就會coredump。本質就是要考慮記憶體洩漏問題,我們可以使用valgrind檢測檢視。