pthread 線程
? ?
?? ?概念 :線程是輕量級進程,一般是一個進程中的多個任務。
? ? ? ? ? ? ? ? 進程是系統中最小的資源分配單位.
? ? ? ? ? ? ? ? 線程是系統中最小的執行單位。
? 優點: 比多進程節省資源,可以共享變量
進程會占用3g左右的空間,線程只會占用一部分,大概8M的空間
進程的父子不會共享,但一個進程之間的線程的資源可以共享.
進程的父子不是平級關系,線程是平級關系
?特征:s's
?? ?1、共享資源
?? ?2、效率高 ?30%
?? ?3、三方庫: pthread ?clone ? posix
?? ??? ??? ?3.1 編寫代碼頭文件: pthread.h
?? ??? ??? ?3.2 編譯代碼加載庫: -lpthread ? library?
?? ??? ??? ?libpthread.so? (linux庫)
?? ??? ??? ?gcc 1.c -lpthread? ? ?-lc
?? ?缺點:
?? ?1,線程和進程相比,穩定性,稍微差些
?? ?2,線程的調試gdb,相對麻煩些。
?? ??? ?info thread?
?? ??? ?*1 ?
?? ??? ?2?
?? ??? ?3
?? ??? ?thread 3?
?? ??? ?
線程與進程區別:
?? ?資源:
?? ??? ?線程比進程多了共享資源。 ?IPC
?? ??? ?線程又具有部分私有資源。
?? ??? ?進程間只有私有資源沒有共享資源。
?? ?空間:
?? ??? ?進程空間獨立,不能直接通信。
?? ??? ?線程可以共享空間,可以直接通信。
? ? ? ?進程解決相對復雜的問題,線 程解決相對復雜的問題.
共同點:
二者都可以并發
3、線程的設計框架 ?posix
?? ?
創建多線程 ==》線程空間操作 ===》線程資源回收
errno ? strerror(errno) ?perror();
??
?3.1 創建多線程:
?? ?int pthread_create(
?? ??? ?pthread_t *thread?,? const pthread_attr_t *attr,
?? ??? ?void *(*start_routine) (void *), void *arg);
?? ?功能:該函數可以創建指定的一個線程。
?? ?參數:thread 線程id,需要實現定義并由該函數返回。
?? ??? ? ?attr ? 線程屬性,一般是NULL,表示默認屬性。
?? ??? ? ?start_routine? ? ??指向指針函數的函數指針。
?? ??? ? ??? ??? ?本質上是一個函數的名稱即可。稱為
th?? ??? ??? ??? ?回調函數,是線程的執行空間。
{
}
?? ??? ? ?arg ?回調函數的參數,即參數3的指針函數參數。
?
?返回值:成功 0
? ? ? ? ? ? ? ? 失敗 錯誤碼
注意:一次pthread_create執行只能創建一個線程。
?? ? ?每個進程至少有一個線程稱為主線程。
?? ? ?主線程退出則所有創建的子線程都退出。暫時先用while(1);?
?? ? ?主線程必須有子線程同時運行才算多線程程序。
?? ? ?線程id是線程的唯一標識,是CPU維護的一組數字。
?? ? ?pstree 查看系統中多線程的對應關系。
?? ? ?多個子線程可以執行同一回調函數。
?? ?ps -eLf 查看線程相關信息Low Weigth Process
?? ?ps -eLo pid,ppid,lwp,stat,comm
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
void *th1(void*arg)
{while(1){printf("發送視頻\n");sleep(1);}
}void *th2(void*arg)
{while(1){printf("接受控制\n");}
}int main(int argc, const char *argv[])
{pthread_t tid1,tid2;pthread_create(&tid1,NULL,th1,NULL);pthread_create(&tid2,NULL,th2,NULL);while(1);return 0;
}
main
?函數開始執行。- 使用?
pthread_create
?創建了兩個線程?tid1
?和?tid2
。 th1
?線程開始執行其無限循環,并在每次迭代中打印 "發送視頻",然后暫停一秒。- 同時(幾乎是同時),
th2
?線程也開始執行其無限循環,不斷打印 "接受控制"。 - 因為兩個線程是并發執行的,所以它們之間沒有固定的打印順序。這取決于操作系統調度器的決策,哪個線程在何時獲得CPU時間片。
main
?函數中的?while(1);
?是一個空循環,它使主線程保持活動狀態,防止程序立即退出。然而,這個空循環并沒有為程序提供任何有用的功能,通常你可能會使用某種形式的線程同步或等待(如?pthread_join
)來確保主線程在所有其他線程完成后才退出。
此時輸出是亂的,是由于
- 線程調度是由操作系統控制的,它決定哪個線程在何時運行。這取決于許多因素,包括線程優先級、系統負載、可用的CPU核心數量等。
- 由于兩個線程都在無限循環中,并且沒有同步機制(如互斥鎖、條件變量等),所以它們會盡可能快地交替執行(或并行執行,如果系統有多個CPU核心),導致輸出看起來沒有規律。
2、pthread_t pthread_self(void); unsigned long int; %lu? 獲取線程號
? ?功能:獲取當前線程的線程id
? ?參數:無
? ?返回值:成功 返回當前線程的線程id
? ??? ??? ??? ?失敗 ?-1;
?? ??? ??? ?syscall(SYS_gettid);
這個方法重啟后失效
alias gcc='gcc -g -pthread '
unalias gcc?
永久起作用
cd ~ //家目錄
vim .bashrc
alias gcc='gcc -g -pthread ' ?:wq
source .bashrc ?生效
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
void *th1 (void*arg)
{while(1){printf("發送視頻 %lu\n",pthread_self());sleep(1);}
}void *th2 (void*arg)
{while(1){printf("接受控制 %lu\n",pthread_self());sleep(1);}
}int main(int argc, char *argv[])
{pthread_t tid1,tid2;pthread_create(&tid1,NULL,th1,NULL);pthread_create(&tid2,NULL,th2,NULL);printf("main th %lu\n",pthread_self());while(1);return 0;
}
- 使用
pthread_create
創建兩個線程:tid1
(運行th1
)和tid2
(運行th2
)。 - 打印主線程的ID。
- 使用
while(1);
使主線程進入無限循環,以保持程序運行。否則,當主線程結束時,程序可能會立即終止,導致其他線程也被終止
練習題:
?? ?設計一個多線程程序,至少有三個子線程
?? ?每個線程執行不同的任務,并實時打印執行
?? ?過程,同時表明身份。
?? ?eg: ./a.out ?==>tid =xxx... ?zheng ...
?? ??? ??? ??? ??? ?tid2 = xxx wozai.
?? ??? ??? ??? ??? ?tid3 = xxx ?wozai ssss
線程的退出:
1.直接用return;? ?
2: 自行退出 ==》自殺 ?==》子線程自己退出
?? ??? ?exit(1);
?? ??? ?void pthread_exit(void *retval); ?exit ?return p;
?? ??? ?功能:子線程自行退出
?? ??? ?參數: retval 線程退出時候的返回狀態,臨死遺言。
?? ??? ?返回值:無
?? ??? ??? ?th
?? ??? ??? ?{
?? ??? ??? ??? ?int a =10;
?? ??? ??? ??? ?pthread_exit(&a);
?? ??? ??? ?}
?? ??? ??? ?join(,&ret)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
void *th1 (void*arg)
{int i =10;while(i--){printf("發送視頻 %lu\n",pthread_self());sleep(1);}pthread_exit(NULL);//return NULL;
}void *th2 (void*arg)
{int i = 10;while(i--){printf("接受控制 %lu\n",pthread_self());sleep(1);}pthread_exit(NULL);
}int main(int argc, char *argv[])
{pthread_t tid1,tid2;pthread_create(&tid1,NULL,th1,NULL);pthread_create(&tid2,NULL,th2,NULL);printf("main th %lu\n",pthread_self());while(1);return 0;
}
? ? 3.?強制退出 ==》他殺 ?==》主線程結束子線程
?? ??? ?int pthread_cancel(pthread_t thread);
?? ??? ?功能:請求結束一個線程? (在主線程種調用 寫入某個線程id號,可以關閉該線程)
?? ??? ?參數:thread 請求結束一個線程tid(想要關閉的線程id號)
?? ??? ?返回值:成功 0
?? ??? ??? ??? ?失敗 -1;
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
void *th1 (void*arg)
{while(1){printf("發送視頻\n");sleep(1);}
}void *th2 (void*arg)
{while(1){printf("接受控制\n");sleep(1);}
}int main(int argc, char *argv[])
{pthread_t tid1,tid2;pthread_create(&tid1,NULL,th1,NULL);pthread_create(&tid2,NULL,th2,NULL);int i = 0 ;while(1){i++;if(3 == i ){pthread_cancel(tid1);}if(5 ==i){pthread_cancel(tid2);}sleep(1);}return 0;
}
作業:
?? ?創建一個多線程程序,至少有10個子線程,
?? ?每個線程有會打印不同的數據,同時表明身份。
?? ?
?? ?線程的回收
?? ?1、線程的回收機制 ====》不同與進程沒有孤兒線程和僵尸線程。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??====》主線程結束任意生成的子線程都會結束。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ====》?子線程的結束不會影響主線程的運行。
?? ?char * retval ; retval++; 1?
?? ?int * retval;?
? ? int pthread_join(pthread_t thread, void **retval); ? ?
? 功能:通過該函數可以將指定的線程資源回收,該函數具有阻塞等待功能,如果指定的線程沒有結束,則回收線程會阻塞。
? 參數:thread ?要回收的子線程tid
? ? ? ? ? ? ??retval ?要回收的子線程返回值/狀態。==》ptread_exit(值);
? 返回值:成功 0
? ? ? ? ? ? ? ? ?失敗 返回一個錯誤號,是一個大于零的數;
? ? ? ? ? ? ? ? ? 失敗可以用
? ? ? ? ? ? ? ?
?
#include <unistd.h>
#include <string.h>
#include <pthread.h>
void *th1 (void*arg)
{int i = 10;while(i--){printf("發送視頻\n");sleep(1);}
}void *th2 (void*arg)
{int i = 10;while(i--){printf("接受控制\n");sleep(1);}
}int main(int argc, char *argv[])
{pthread_t tid1,tid2;pthread_create(&tid1,NULL,th1,NULL);int ret = pthread_create(&tid2,NULL,th2,NULL);if(ret!=0){// perror()fprintf(stderr,"error %s\n",strerror(ret));//exit();}pthread_join(tid1,NULL);pthread_join(tid2,NULL);return 0;
}
子線程的回收策略:
? 1、如果預估子線程可以有限范圍內結束則正常用pthread_join等待回收。
? 2、如果預估子線程可能休眠或者阻塞則等待一定時間后強制回收。
? 3、如果子線程已知必須長時間運行則,不再回收其資源。
??
??
? 線程的參數,返回值
??
1、傳參數
?? ??? ?
?? ?傳整數 ===》int add(int a,int b); ?///a b 形參
?? ??? ??? ??? ?add(x,y); ? ?x y 實參?
?? ??? ?pthread_create(&tid,NULL,fun,x);
?? ??? ?fun ==>void * fun(void * arg);
?? ??? ?
?? ?練習:創建一個子線程并向該線程中傳入一個字符在
?? ??? ? ?線程中打印輸出。
?? ??? ? ?在此基礎上向子線程中傳入一個字符串,并在
?? ??? ? ?子線程中打印輸出。
?? ??? ? ?
?? ??? ? ?add(int a, int b)
?? ??? ? ?{
?? ??? ??? ?int c = a+b;
?? ??? ??? ?char buf[]=""
?? ??? ??? ?return c;
?? ??? ? ?}
?? ??? ??? ??? ??? ?5
?? ??? ? ?int d = add(2,3);
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>void* th(void* arg)
{static int a =20;return &a;
}int main(int argc, char *argv[])
{pthread_t tid;void* ret;pthread_create(&tid,NULL,th,NULL);pthread_join(tid,&ret);printf("ret %d\n",*(int*)ret);return 0;
}
?? ?傳字符串
?? ??? ?棧區字符數組:
?? ??? ?字符串常量:
?? ??? ?char *p = "hello";
?? ??? ?堆區字符串;
?? ??? ??? ?char *pc = (char *)malloc(128);
?? ??? ??? ?ptread_create(&tid,NULL,fun,pc);
?? ??? ??? ?pthread_join(tid,NULL);
?? ??? ??? ?free(pc);
?? ??? ?
?? ??? ??? ?fun(void *arg)
?? ??? ??? ?{
?? ??? ??? ??? ?char * pc = (char *)arg?? ?;
?? ??? ??? ??? ?printf("%s \n",pc);
?? ??? ??? ??? ??? ??? ?%c
?? ??? ??? ?}
棧區
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>void* th(void* arg)
{static char buf[256]={0};strcpy(buf,"要消亡了\n");return buf;
}int main(int argc, char *argv[])
{pthread_t tid;void* ret;pthread_create(&tid,NULL,th,NULL);pthread_join(tid,&ret);printf("ret %s\n",(char*)ret);return 0;
}
堆區:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>void* th(void* arg)
{char * tmp = (char* )arg;strcpy(tmp,"hello");return tmp;
}int main(int argc, char *argv[])
{pthread_t tid;char * p = (char*)malloc(50);void* ret;pthread_create(&tid,NULL,th,p);pthread_join(tid,&ret);printf("ret %s\n",(char*)ret);free(p);return 0;
}
??
?傳結構體
?? ?1、定義結構體類型
?? ?2、用結構體定義變量
?? ?3、向pthread_create傳結構體變量
?? ?4、從fun子線程中獲取結構體數據
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
typedef struct
{char * p;int a;
}TH_ARG;
void* th(void* arg)
{TH_ARG * tmp = (TH_ARG* )arg;strcpy(tmp->p,"hello");//strcpy( ((TH_ARG*)arg)->p ,"hello");tmp->a +=10;return tmp;
}int main(int argc, char *argv[])
{pthread_t tid;int a =20;char * p = (char*)malloc(50);TH_ARG arg;arg.a = a;arg.p = p;void* ret;pthread_create(&tid,NULL,th,&arg);pthread_join(tid,&ret);printf("ret %s %d\n",((TH_ARG*)ret)->p,((TH_ARG*)ret)->a);free(p);return 0;
}
練習:
?? ?定義一個包含不同數據類型的測試結構體
?? ?并向子線程傳參數,同時在子線程中打印輸出。
?? ?定義一個回調函數可以完成計算器的功能
?? ?定義一個數據結構體可以一次傳入不同的數據
?? ?和計算方式并將結果打印輸出。
?? ?//2 + 3.6?
?? ?// 2 + 3 ?2+3
?? ?// 8 * 6
?? ?typedef strcut
?? ?{
?? ??? ?float a;
?? ??? ?float b;
?? ??? ?char c;//+ - * /?
?? ??? ?float d;
?? ?}JSQ;
?? ?
?? ?
返回值:pthread_exit(0) ===>pthread_exit(9);
?? ??? ?pthread_join(tid,NULL); ===>pthread_join(tid,?);
10;
-10;
int * p =malloc(4);
*p = -10;
1、pthread_exit(?) ==>? = void * retval;
?? ??? ??? ??? ??? ??? ? ?純地址
2、pthread_join(tid,?) ==>? = void **retval;
?? ??? ??? ??? ??? ??? ??? ?地址的地址
原理:子線程退出的時候,可以返回一個內存地址
?? ? ?改值所在的內存中可以存儲任何數據,只要
?? ? ?地址存在,則數據都可以正常返回。
?? ?
?? ?地址有三種:
?? ?0、棧區變量 ?錯誤,子線程結束該地址失效。
?? ?1、全局變量 ?失去意義,本質可以直接訪問。
?? ?2、靜態變量?
?? ?3、堆區變量
?? ? ?主線程通過一個地址形式的變量來接受子進程
?? ? ?返回的地址變量就可以將該地址中的數據取到。
?? ?練習:從子線程中申請一塊堆區內存并存字符串
? ? ? 將該字符串以返回值形式返回到主線程并打印輸出。
?? ? ??? ?
?
? ?設置分離屬性,目的線程消亡,自動回收空間。
??
主線程沒有空,才設置分離屬性來回收.
?attribute
?int pthread_attr_init(pthread_attr_t *attr);
?? ?功能,初始化一個attr的變量
?? ?參數:attr,需要變量來接受初始值
?? ?返回:0 ?成功,
?? ?非0 錯誤;
? ? ? ?int pthread_attr_destroy(pthread_attr_t *attr);
?? ? ?功能:銷毀attr變量。
?? ? ?attr,屬性變量
?? ? ?返回:0 ?成功,
?? ?非0 錯誤;
?? ? ??
?? ? ??
?? ?man -k?
?? ? int pthread_attr_setdetachstate(pthread_attr_t *attr
, int detachstate);
?? ?功能:把一個線程設置成相應的屬性
?? ?參數,attr,屬性變量,有init函數初始化他。
?? ?detachstate:有2個可選值,
?? ?
?? ?PTHREAD_CREATE_DETACHED:設置分離屬性。
?? ?
?? ?第二種設置分離屬性:
int pthread_deatch(pthread_t thread);
?? ?功能,設置分離屬性
?? ?參數,線程id號,填自己的id
?? ?
?? ?do{
?? ?
?? ?
?? ?}while()
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
void* th(void* arg)
{pthread_detach(pthread_self());return NULL;
}int main(int argc, char *argv[])
{pthread_t tid;int i = 0 ;for(i=0;i<50000;i++){int ret = pthread_create(&tid,NULL,th,NULL);if(ret!=0){break;}// pthread_detach(tid);}printf("%d \n",i);return 0;
}
void pthread_cleanup_push(void (*routine)(void *), void *arg);
?? ?功能:注冊一個線程清理函數
?? ?參數,routine,線程清理函數的入口
?? ??? ?arg,清理函數的參數。
?? ?返回值,無
?? ??? ?
void pthread_cleanup_pop(int execute);
?? ?功能:調用清理函數
?? ?execute,非0 ?執行清理函數
?? ??? ??? ?0 ,不執行清理
?? ??? ??? ?
?? ?返回值,無
do
{
}while(1)
process?? ??? ??? ??? ?thread
fork?? ??? ??? ??? ?pthread_create?
getpid,ppid,?? ??? ?pthread_self
exit,?? ??? ??? ??? ?pthread_exit?
wait,waitpid,?? ??? ?pthread_join?
kill,?? ??? ??? ??? ?pthread_cancel
atexit ?? ??? ??? ??? ?pthread_clean,
exec?? ??? ??? ??? ?system--->fork->exec (ls)
?? ??? ??? ??? ??? ?
?