執行緒,多執行緒與同步互斥
程序:只是分配資源的單位 , 不執行指令(是而靠執行緒執行指令)
執行緒 (#include<pthread.h>) (Thread輕量級的程序):程序內部的一條執行路徑(俗稱程序的輕量級)
主執行緒:每一個程序必須有一個預設執行緒,稱為主執行緒(執行主函式)
執行緒函式:執行緒執行指令的指令集(函式)
執行緒屬性程序的:執行緒是程序的資源,程序退出,該程序的執行緒就會退出。
併發:同時執行
單CPU:分時複用CPU
多CPU:每一個CPU執行一個執行緒,同時執行
執行緒的tid:執行緒的id標識
執行緒是共享程序的資源,執行緒之間通訊比較。
執行緒也是有私有空間(佔很小空間:tid...)
API:
建立子執行緒:是由主執行緒建立執行緒,
API : int pthread_create ( pthread_t *thread , const pthread_attr_t *attr , void * ( *start_routine) (void *) , void *arg);
thread:執行緒的id標識(不能為空)
attr:執行緒的屬性: NULL 不獲取屬性
start:執行緒的函式
arg:傳遞的引數 NULL:傳遞空值
執行緒的tid:
pthread_t pthread_self(void);
主執行緒等待子執行緒的結束:
ptrhead_join
程序和執行緒的區別和特點:
1.執行緒屬於程序,即一個程序內部可以有多個執行緒,至少有一個執行緒(即主執行緒)
2.程序是作業系統分配資源的基本單位,沒有執行程式碼的能力,程式碼是由執行緒去執行的,執行緒是作業系統分配CPU時間片的基本單位
3.一個程序內部的所有執行緒共享程序的所有資源,所以執行緒間通訊非常簡單
4.每個執行緒都有一個唯一的ID,稱為執行緒ID(即TID)
5.每個執行緒都必須對應一個執行緒函式,執行緒建立後就會自動去呼叫它的執行緒函式,執行緒函式返回,這個執行緒就結束了
6.主函式可以認為是主執行緒的執行緒函式,一個程序被建立後,系統會自動建立它的主執行緒,主執行緒會自動去呼叫其執行緒函式(即主函式),主函式返回主執行緒就結束了,程序也就結束了
在單CPU系統中的所有執行緒併發執行,分時複用CPU
在多CPU系統中,比如有N個CPU,可以允許系統中N個執行緒並行執行
併發:巨集觀上同時執行,微觀某一時刻只有一個執行緒在執行,其他執行緒都在暫停
並行:微觀某一時刻有多個執行緒在同時執行
併發:多個任務同時執行
同步:為了共同完成某個任務,規定一種先後的次序。( 生產者/消費者 )
互斥:每次只能有一個任務執行,其它任務必須等待完成才能爭搶使用。( 讀/寫者 )
資料汙讀:bool 的 true 和 bool 的 false , 當1執行緒執行true時讀檔案, 為false時2執行緒讀檔案,但是由於執行緒的cpu分時複用,可能還會執行 true 操作 , 導致讀入資料異常 . 執行緒之間資料共享:
汙讀:
A--(讀)-->data
B--(寫)-->data
汙寫: A--(true) bool flag B--(false) bool flag
解決方法:原子性 : 就是在執行讀操作的時候需要把讀操作執行完畢 , 執行寫操作的時候需要把寫操作執行完畢 .
主執行緒和普通執行緒的區別:
1.主執行緒是由系統自動建立,而其他普通執行緒是由主執行緒直接或間接建立
2.主執行緒結束會導致程序結束,程序內部的所有正在執行的執行緒必須強制結束,而一個普通執行緒結束對其他執行緒和程序不會產生任何影響.
執行緒管理相關的函式:
建立執行緒:pthread_create
結束執行緒:pthread_exit, pthread_cancel
等待執行緒結束:pthread_join
獲取當前執行緒ID:pthread_self
問:一個程序能建立大量的執行緒嗎?
當系統中執行緒很多時,系統會忙於執行緒間的切換工作(保護現場和恢復現場),會有大量的時空開銷,導致系統性能下降,所以不建議建立太多執行緒.
多執行緒同步和互斥控制:
1.互斥量(Mutex)(互斥控制) : 當一個互斥量被某個執行緒加鎖成功後,其他執行緒對它加鎖時就會被阻塞,直到該互斥量被解鎖為止
鎖:只有一個資源 初始化鎖 : pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 建立鎖 : pthread_mutex_lock(&mutex); 解除鎖 : pthread_mutex_unlock(&mutex); 釋放鎖 : pthread_mutex_destroy(&mutex); (第二種初始化鎖 : 函式外宣告 : static pthread_mutex_t mutex; 在main中初始化鎖 : pthread_mutex_init(&mutex,NULL);)
2.訊號量(#include<semaphore.h>) P V 操作
當一個互斥量被某個執行緒加鎖成功後,其他執行緒對它加鎖時就會被阻塞,直到該互斥量被解鎖為止。
訊號量是一個特殊的整數值,支援P操作(即wait操作,讓訊號量減1)和V操作(即post操作,讓訊號量加1)
如果訊號量的值已經為0,P操作將會阻塞(Block),直到訊號量的值大於0 . 初始化訊號量(初始化資源的數量)
P 資源數-1 當資源<0,則等待 sem_wait
v 資源數+1 sem_post
銷燬訊號量 sem_destroy(&sa);
執行緒釋放:
每一個執行緒在任何情況,要麼是可結合的狀態(joinable),要麼是可分離的狀態(detached)。
先將這兩個函式的原型列一下
int pthread_join(pthread_t tid, void ** pthread_return);
int pthread_detach(pthread_t tid);
當我們的執行緒執行結束後,最後顯示的呼叫被回收。這樣就出現兩種回收方式。
1. pthread_join是一個阻塞函式,呼叫方會阻塞到pthread_join所指定的tid的執行緒結束後才被回收,但是在此之前,呼叫方是霸佔系統資源的。
2. pthread_detach,不會阻塞,呼叫它後,執行緒執行結束後會自動釋放資源。
PS:一個可結合線程在執行結束後,若沒有呼叫pthread_join,會進入一個類似zombie process的狀態,也就是系統中還有一些資源沒有回收。需要pthread_join來回收這些資源。(這就類似程序操作中的waitpid函式)
注:如果父執行緒要等待子執行緒結束而結束 ,子執行緒與父執行緒為結合狀態(必須由執行緒呼叫pthread_join來釋放)
建立執行緒 :
#include<stdio.h>
#include<pthread.h>
//執行緒函式
void* fun(void* arg)
{
printf("%s\n",(char*)arg);
}
int main()
{
pthread_t t;
char buf[100]="hello";
//建立執行緒
pthread_create(&t,NULL,fun,buf);
//等待執行緒
pthread_join(t,NULL);
}
建立多執行緒的例項 :
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
int i=100;
//定義執行緒的函式
void* threadfun(void* arg)
{
sleep(2);
printf("我是子執行緒,我的ID:%x i=%d\n",pthread_self(),i);
//結束執行緒:
//pthread_exit(NULL);
return NULL;
sleep(5);
printf("子執行緒結束啦\n");
return NULL;
}
//程序:分配資源
int main()
{
/*
int i=0;
for(i=1;i<=100;i++)
{
printf("%d \n",i);
sleep(1);
exit(0);//程序退出,執行緒都會退出
}
printf("\n");
*/
pthread_t tid=pthread_self();
printf("主執行緒的ID:%x i=%d\n",tid,i);
//建立執行緒:
pthread_t ctid;
pthread_create(&ctid,NULL,threadfun,NULL);
//改變i的值
i=79;
printf("主執行緒i=%d\n",i);
//等待子執行緒結束
pthread_join(ctid,NULL);
printf("主執行緒等待子執行緒結束啦,\n");
}
初始化鎖 , 建立鎖 , 解鎖 , 消除鎖 :
#include<stdio.h>
#include<pthread.h>
//全域性變數
//static pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER; //初始化鎖的一種
static pthread_mutex_t mutex; //初始化鎖前的宣告
void* threadFun(void* arg)
{
//程序的序號
int num=(int)arg;
/*臨界區*/
//上鎖
pthread_mutex_lock(&mutex);//上鎖後 , 必須等待
printf("%d號進洗手間\n",num);
sleep(1);
printf("%d號出洗手間\n",num);
pthread_mutex_unlock(&mutex);//解鎖
/*臨界區*/
}
int main()
{
//初始化
pthread_mutex_init(&mutex,NULL); //初始化鎖
//建立4個子執行緒
pthread_t tid[4];
int i=0;
for(i=0;i<4;i++)
{
pthread_create(tid+i,/*TID*/NULL,/*屬性*/threadFun,/*執行緒函式*/(void*)i/*傳遞的值*/);
}
//等待子執行緒結束
for(i=0;i<4;i++)
pthread_join(tid[i],NULL);
//銷燬鎖
pthread_mutex_destroy(&mutex);
return 0;
}
訊號量的建立初始化,sem_wait(); sem_post();
#include<stdio.h>
#include<semaphore.h>
#include<pthread.h>
sem_t sem;
//執行緒的函式
void* threadfun(void* arg)
{
short num=(int)arg;
/*臨界區*/ //把訊號量比作洗手間:數量為3
sem_wait(&sem); //P 向訊號量減1 當訊號量小於0的時候則阻塞等待
printf("%d的進入洗手間\n",num);
sleep(3);
printf("%d退出洗手間\n",num);
sem_post(&sem);//V 資源數+1
return NULL;
}
int main()
{
//初始化訊號量值 (訊號量,預設0,資源數3)
sem_init(&sem,0,3);
//1建立5個執行緒
pthread_t tid[5];
int i=0;
for(i=0;i<5;i++)
pthread_create(tid+i,NULL,threadfun,(void*)i);
//2等待執行緒
for(i=0;i<5;i++)
pthread_join(tid[i],NULL);
//銷燬訊號量
sem_destroy(&sem);
return 0;
}
多執行緒拷貝檔案(個人做法,可能有問題,可以拷貝) :
#include<iostream>
#include<pthread.h>
#include<errno.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
using namespace std;
//全域性變數
int fdr;
int fdw;
long total;
long ilen;
long ave;
char buf[1024]="";
static pthread_mutex_t mutex;
void* threadfun(void* arg)
{
int num=(int)arg;
pthread_mutex_lock(&mutex); //必須等待
total=0;
while(total<ave && ((ilen=read(fdr,buf,1024))>0))
{
write(fdw,buf,ilen);
total+=ilen;
}
pthread_mutex_unlock(&mutex);
}
void* threadfun2(void* arg)
{
int num=(int)arg;
pthread_mutex_lock(&mutex); //必須等待
while((ilen=read(fdr,buf,1024))>0)
{
write(fdw,buf,ilen);
}
pthread_mutex_unlock(&mutex);
}
int main(int argc,char* argv[])
{
if(argc<3)
{
cout<<"引數不足"<<endl;
return -1;
}
fdr=open(argv[1],O_RDONLY);
fdw=open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0644);
if(-1==fdr||-1==fdw)
{
perror("fail");
return -1;
}
ilen=lseek(fdr,0,SEEK_END);
lseek(fdr,0,SEEK_SET);
ave=ilen/2;
//初始化
pthread_mutex_init(&mutex,NULL);
//建立2個子執行緒
pthread_t tid[2];
int i=0;
for(i=0;i<2;i++)
{
if(0==i)
{
pthread_create(tid+i,NULL,threadfun,(void*)i);
}
//等待執行緒結束
pthread_join(tid[i],NULL);
if(1==i)
{
close(fdr);
close(fdw);
fdr=open(argv[1],O_RDONLY);
fdw=open(argv[2],O_WRONLY);
lseek(fdr,ave,SEEK_SET);
lseek(fdw,ave,SEEK_SET);
pthread_create(tid+i,NULL,threadfun2,(void*)i);
close(fdr);
close(fdw);
}
}
//銷燬鎖
pthread_mutex_destroy(&mutex);
return 0;
}