1. 程式人生 > >執行緒,多執行緒與同步互斥

執行緒,多執行緒與同步互斥

程序:只是分配資源的單位 , 不執行指令(是而靠執行緒執行指令)
執行緒 (#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;
}