歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Linux 多線程編程( POSIX )

Linux 多線程編程( POSIX )

日期:2017/3/1 10:26:00   编辑:Linux編程
1.基礎線程創建:

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

void * print_id( void * arg) //!>這是線程的入口函數
{
printf("TheCurrent process is: %d \n",getpid()); //!>當前進程ID
printf( "TheCurrent thread id : %d \n", (unsigned)pthread_self()); //!> 注意此處輸出的子線程的ID
}

int main( )
{
pthread_t t;
int t_id;

t_id =pthread_create( &t, NULL, print_id, NULL); //!> 簡單的創建線程

if( t_id !=0 ) //!>注意創建成功返回0
{
printf("\nCreate thread error...\n");
exit(EXIT_FAILURE );
}
sleep( 1);
printf("\nThe Current process is: %d \n",getpid()); //!>當前進程ID
printf( "TheMain thread id : %d \n", (unsigned)pthread_self()); //!> 注意輸出的MAIN線程的ID
return0;
}



2.測試線程的創建和退出

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

void * entrance_1( void * arg) //!> 第一個創建的線程的入口函數
{
printf( "thread 1 id == %d , run now ... \n", ( unsigned )pthread_self());
sleep( 3);
return ( (void * ) 1 );
}

void * entrance_2( void * arg) //!> 第二個創建的線程的入口函數
{
printf( "thread 2 id == %d , run now ... \n", ( unsigned )pthread_self());
sleep( 3);
return ( (void * ) 2 );
}

int main( )
{
pthread_t t1 =-1; //!> 最好是初始化:因為下面的pthread_join是需要判斷是否成功在輸出的
pthread_t t2 =-1;
int tid1;
int tid2;
void * ret;

tid1 =pthread_create( &t1, NULL, entrance_1, NULL); //!> 簡單的創建線程
tid2 =pthread_create( &t2, NULL, entrance_2, NULL);

if( tid1 !=0 || tid2 != 0) //!>創建線程失敗
{
printf("Create thread error...\n" );
exit(EXIT_FAILURE );
}

if( t1 != -1) //!> 也就是線程還沒有結束
{
if (pthread_join( t1, &ret ) == 0) //!> join success
{
printf( "thread 1 get the return of pthread_join == %d \n", 2 );/ )
{
pthread_mutex_init( &mutex, NULL); //!> 初始化為默認的互斥鎖

printf("主函數:創建2個子線程...\n");

create_two_thread(); //!> 創建2個線程

printf("主函數:等待線程完成任務...\n");

wait_two_thread(); //!> 等待線程完成任務
//!> 線程任務完成才可以執行下面代碼
printf("線程任務完成...\n");

printf("Num == %d \n\n", num);

return 0;
}

4.雙線程處理:冒泡排序算法

// 雙線程處理冒泡排序(多線程也一樣)
// 實現從“小”--->“大”排序

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

int g_arr[] = { 10, 23, 12, 34, 5, 29, 90, 9, 78, 44}; //!> 全局的要排序的數組
pthread_t thread[2]; //!> 兩個線程
pthread_mutex_t mutex; //!> 互斥鎖

int g_i =0; //!> 全局的剩余排列次數

//!> 打印數組
void print_array()
{
int i;
for( i = 0;i < 10; i++ )
{
printf( " %d", g_arr[i] );
}
printf("\n");
}

//!> 交換元素
void swap_elem( int * a, int * b )
{
inttemp;
temp =*a;
*a =*b;
*b =temp;
}

//!> 線程1入口函數
void * entrance_1( void * arg )
{
int j;
for( g_i =0; g_i < 10; g_i++) //!> 外層循環
{
pthread_mutex_lock( &mutex); //!> 加鎖

printf("線程1後台執行排序...\n" );

for( j = 0;j < ( 10 - g_i - 1 ); j++) //!> 內層循環
{
if( g_arr[j]> g_arr[j+1] )
{
swap_elem(&g_arr[j], &g_arr[j+1] );
}
}

pthread_mutex_unlock( &mutex); //!> 解鎖

sleep( 1);
}
}

//!> 線程2入口函數
void * entrance_2( void * arg )
{
int j;
for( g_i =0; g_i < 10; g_i++) //!> 外層循環
{
pthread_mutex_lock( &mutex); //!> 加鎖

printf("線程2後台執行排序...\n" );

for( j = 0;j < ( 10 - g_i - 1 ); j++) //!> 內層循環
{
if( g_arr[j]> g_arr[j+1] )
{
swap_elem(&g_arr[j], &g_arr[j+1] );
}
}

pthread_mutex_unlock( &mutex); //!> 解鎖

sleep( 2);
}
}

//!> 創建2個線程
void create_two_thread()
{
memset(&thread, 0, sizeof( thread )); //!> 初始化為0(作為下面的判斷進程是否創建OK依據)

if( (pthread_create( &thread[0], NULL, entrance_1, NULL) ) == 0 )
{
printf("線程1創建OK ...\n");
}
else
{
printf("線程1創建Error ...\n");
exit(EXIT_FAILURE );
}

if( (pthread_create( &thread[1], NULL, entrance_2, NULL) ) == 0 )
{
printf("線程2創建OK ...\n");
}
else
{
printf("線程2創建Error ...\n");
exit(EXIT_FAILURE );
}

}

//!> 線程執行與等待
void do_and_wait()
{
if(thread[0] != 0 )//!>由於在create_two_thread中初始化=0,if床架ok,那麼不可能還是0
{
pthread_join( thread[0], NULL); //!> 等待線程1結束,不結束不執行下面代碼
printf("線程1執行結束退出...\n");
}
else
{
printf("線程1創建Error...\n");
exit(EXIT_FAILURE );
}

if(thread[1] != 0 )
{
pthread_join( thread[1], NULL); //!> 等待線程1結束,不結束不執行下面代碼
printf("線程2執行結束退出...\n");
}
else
{
printf("線程2創建Error...\n");
exit(EXIT_FAILURE );
}

}

int main( )
{
printf("主函數:下面創建2個線程共同處理冒泡排序...\n");

pthread_mutex_init( &mutex, NULL );

print_array(); //!> 打印排序前的結果

create_two_thread(); //!> 創建線程
do_and_wait(); //!> 執行線程and等待

printf("排序完成:\n" );

print_array(); //!> 打印排序後的結果

return0;
}

5.線程清理處理程序

// 線程清理處理程序TEST

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

void clean(void *arg)
{
printf("清理:%s \n", (char *)arg);
}

void * entrance( void * arg )
{
printf("線程開始...\n");

pthread_cleanup_push( clean, "線程處理程序1" );
pthread_cleanup_push( clean, "線程處理程序2" );

printf("pthread clean 完成...\n");

sleep(3);

pthread_exit((void*)0); //!> 我們知道:清理函數只有在異常退出時候才會做一些清理工作
//!> 所以此處的退出是異常退出來測試的!

pthread_cleanup_pop(0);
pthread_cleanup_pop(0);



}

int main( )
{
pthread_t tid;
void * ret = NULL;

if( (pthread_create( &tid, NULL, entrance, (void *)1 ) )!= 0 )
{
printf("創建線程失敗...\n");
exit(EXIT_FAILURE );
}

pthread_join( tid, &ret);

if( ret) //!> 注意此處相當於是拋出異常
{ //!> 避免子線程的異常退出造成的空指針情況
printf("結束:code == %d\n", *( ( int * ) ret) );
}

return0;
}

/////////////////////////////////////////////////////////////
// DEMO——2

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

void clean( void * arg )
{
printf("清理函數執行...\n");
}

void * entrance( void * arg )
{
intold_type, old_state;
int i =0;

pthread_cleanup_push( clean, NULL); //!> 設置清理函數
printf("下面設置對本線程的“取消”無效\n");
pthread_setcancelstate( PTHREAD_CANCEL_DISABLE,&old_state );
//!> 設置對本線程的“取消”無效
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,&old_type); //>>>>>>>>>>>目標句1

while( 1)
{
++i;
printf("子線程runing...\n");
sleep( 2);
if( 5 == i)
{
printf("下面取消設置對本線程的“取消”無效\n");
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,&old_state);
}
}

pthread_cleanup_pop( 0 );
}

int main( int argc, char ** argv )
{
pthread_t tid;
int res;
void* ret;

pthread_create( &tid, NULL, entrance, NULL );
sleep( 2);
printf("請求子線程退出...\n");
pthread_cancel( tid); //!> 請求子線程退出

res =pthread_join( tid, &ret); //!> 等待子線程退出

if( ret !=PTHREAD_CANCELED) //!> 非安全退出
{
printf("pthread_join 失敗...\n");
exit(EXIT_FAILURE );
}
else
{
printf("Success..");
}

exit(EXIT_SUCCESS);
}


分析:
沒有加上“目標句”的結果是:
下面設置對本線程的“取消”無效
子線程runing...
請求子線程退出...
子線程runing...
子線程runing...
子線程runing...
子線程runing...
下面取消設置對本線程的“取消”無效
子線程runing... //!> 比下面多的
清理函數執行...
Success.. //!> 與下面不一樣的

加上後:
下面設置對本線程的“取消”無效
子線程runing...
請求子線程退出...
子線程runing...
子線程runing...
子線程runing...
子線程runing...
下面取消設置對本線程的“取消”無效
清理函數執行...
pthread_join失敗... //!> 與上面不一樣的

這句的作用是將取消類型設置為PTHREAD_CANCEL_ASYNCHRONOUS,即取消請求會被立即響應
那麼就不會再次進入等待下一個“取消點”再進行取消!!!

注意:if在while中沒有sleep,那麼程序會無限run,我們知道sleep是相當於是釋放一下線程,那麼此時的主線程中的cancel信號又被接收到,那麼if本函數可以響應了,那麼就cancle了,if將sleep去掉,那麼死循環!再次解釋:所謂pthread_cancel僅僅是請求某個線程退出,那麼究竟是不是退出還要看state和type的設置!



6. pthread_once工作原理code


// pthread_once函數使用

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

pthread_once_t once =PTHREAD_ONCE_INIT; //!> once宏賦值

//!> 初始化執行函數
void once_init( void )
{
printf("初始化成功! 我的ID == %d\n", (unsigned)pthread_self());
}

//!> 線程入口函數
void * entrance( void * arg )
{
printf("子線程:ID == %d \n", (unsigned)pthread_self());
//!> once =PTHREAD_ONCE_INIT; //!> 測試使用(下面的要求)
pthread_once( &once, once_init); //!> 此處也有初始化
}

//!> main函數
int main( int argc, char * argv[] )
{
pthread_t pid;

pthread_create( &pid, NULL, entrance, NULL );

printf("主函數ID == %d \n", (unsigned)pthread_self());

//!> pthread_join( pid, NULL); //!> 分析點

pthread_once( &once, once_init); //!> 調用一次初始化函數

pthread_join( pid, NULL );

return0;
}

ifpthread_join是在主函數初始化後面,那麼就是主函數初始化的
結果是: 主函數ID== 441960192
初始化成功! 我的ID == 441960192
子線程:ID == 433944320
顯而易見是主函數初始化的!

ifpthread_join是在之前,那麼就是要等待子函數執行ok後才執行自己的下面代碼
但是此時已經初始化ok了,所以不在初始化!
結果是:主函數ID ==210818816
子線程:ID == 202802944
初始化成功! 我的ID == 202802944
顯然是子函數執行的初始化!

本質: 其實就是操作once變量而已,與互斥變量的本質是一樣的!!!
我們可以這樣測試在entrance中加入once = PTHREAD_ONCE_INIT;
結果是:主函數ID ==1590228736
初始化成功! 我的ID == 1590228736
子線程:ID == 1582212864
初始化成功! 我的ID == 1582212864

感興趣的可以使用pthread_mutex_t 的互斥變量處理,效果一樣!
還有最最簡單的就是bool值處理!此處不建議!

7.pthread_key_create線程鍵 與線程存儲

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

pthread_once_t once =PTHREAD_ONCE_INIT;
pthread_key_t key; //!> 鍵值

int g_val =10; //!> 傳說中的獨享值,呵呵

void once_init_key()
{
if(pthread_key_create( &key, NULL ) == 0) //!> 創建線程鍵值
{ //!>
printf("創建線程鍵OK ...\n");
}
}

void * entrance( void * arg )
{
int *val;
printf("子線程:ID == %d \n", (unsigned)pthread_self());

pthread_setspecific( key, &g_val); //!> 將 g_val 作為一個每個進程的獨享值

val = ( int* )pthread_getspecific( key); //!> 取出那個值
//!> 此後對於這些量都有自己的處理方式,
//!>名稱相同但是內容不同!!!
//!> 對於文件的處理是最好的!!!
printf("ID== %d, Value == %d\n", (unsigned)pthread_self(),*val);
}


int main( )
{
pthread_t tid,tid2;
void * ret1;
void * ret2;

if(pthread_create( &tid, NULL, entrance, NULL ) != 0) //!> 線程1
{
printf("創建線程1失敗...\n");
exit(EXIT_FAILURE );
}

if(pthread_create( &tid2, NULL, entrance, NULL ) != 0) //!> 線程2
{
printf("創建線程2失敗...\n");
exit(EXIT_FAILURE );
}

printf("主函數:ID == %d \n", (unsigned)pthread_self());

//!>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

pthread_once( &once, once_init_key); //!> 創建一個鍵值

//!>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

printf("下面等待子線程執行ok... \n");

pthread_join( tid, &ret1); //!> 等待線程( 必不可少 )
pthread_join( tid2, &ret2 );

return0;
}

結果:
主函數:ID ==1588877056
創建線程鍵OK...
子線程:ID ==1580861184
ID ==1580861184, Value == 10
下面等待子線程執行ok...
子線程:ID ==1572468480
ID ==1572468480, Value == 10
Copyright © Linux教程網 All Rights Reserved