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_ptr
給pthread_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;
}
解決方法有三個:
- 執行緒裡面呼叫 pthread_detach(pthread_self()) 這個方法最簡單
- 在建立執行緒的設定PTHREAD_CREATE_DETACHED屬性
- 建立執行緒後用 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檢測檢視。