线程,多线程与同步互斥

版权声明:请在征得作者同意的情况下,可以进行非盈利性引用。引用请注明出处:“作者:慕华思弦 转载地址” 字样,以尊重作者的劳动成果,并保持良好的版权意识。 https://blog.csdn.net/Superman___007/article/details/82858369

进程:只是分配资源的单位 , 不执行指令(是而靠线程执行指令)
线程 (#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;
}

猜你喜欢

转载自blog.csdn.net/Superman___007/article/details/82858369