Linux:多线程基础概念

多线程:

    概要信息:认识线程    线程控制    线程安全    线程池

1.认识线程;
  -1.什么是线程?
      线程是一个独立执行流(运行代码,处理数据)。
  -2.线程概念;
      先说进程:传统操作系统中使用pcb来描述一个个程序的运行-pcb就是进程
      linux下线程使用进程pcb模拟实现线程,进程为线程组。因此linux下的线程实际是一个轻量级进程lwp
      这个轻量级进程因为共用大部分进程资源,相较于传统进程更加轻量化
  -3.进程是资源分配的基本单位---因为程序运行时资源是分配给整个线程组(进程)的
       线程是cpu调度的基本单位---因为linuxpcb是线程

2.线程之间资源的独有与共享
    -1.独有:每个线程在虚拟地址空间中都有自己独立的空间(栈),cup内寄存器,信号屏蔽字,errno(全局变量)
,线程ID,调度优先级
    -2.共享虚拟地址空间,文件描述符,信号处理方式,当前工作路径,用户id/组id
    -3.多任务的执行-既可以使用多进程也可以使用多线程,哪一个更好。
         分析优缺点--》视场景而定
         多线程任务处理的优缺点
          优点:  -多线程共用进程大部分资源
               线程间通信处理进程间的方式之外还有更简单的就是全局数据/传参---线程间通信更加方便
               创建/销毁一个线程的成本相较于进程要低
               线程间的调度相较于   进程要更低
          缺点:线程之间缺乏访问控制,有些系统调用/异常针对的是整个进程;稳定性相较于进程低。
          使用场景:让子进程背锅的时候用多进程。shell这种对主程序稳定安全性要求更高的程序就需要使用多进程,让
子进程处理任务。

3.线程控制:
分为:线程创建/线程终止/线程等待/线程分离
   linux下操作系统并没有提供线程的控制系统调用接口;因此大佬们封装了一个套线程控制接口库。
   使用库函数实现创建的线程我们称之为用户态线程,这个用户态线程在内核中使用一个轻量级进程实现调度
   linux下的线程:用户态线程+轻量级进程

    -1.线程创建
      pthread_t tid
      int pthread_create(pthread_t*thread,       const pthread_attr_t*attr,    void*(*start_routine)(void*),  void*arg);
     
      thread:用于获取线程id  线程地址空间在整个虚拟地址空间的首地址;
      attr: 设置线程属性,通常置NULL
      start_routine:线程的入口函数
      arg:传递给线程函数的参数。
      返回值:成功返回  0;失败返回错误编号(非零)。

 

 /*通过线程创建的demo体会每一个线程都是一个执行流让,
         每一个线程都运行以恶搞死循环,查看是否都能够打印数据*/
   	 #include
	#include
                  #include

	void thr_entry(void*arg)
	{

		while(1)
		{
			printf("i am common thread---%s\n",(char*)arg);
			sleep(1);
		}
	return NULL;
	}

	int main()
	{
  	 	pthread_t tid;
		char *param="THIS is input param";
		int ret = pthread_create(&tid,NULL,thr_entry,(void*)param);
		if (ret!=0)
		{
			printf("pthread create error\n");	
			return -1;
		}
	
		while(1)
		{
   			printf("i am main thread-----\n");
			sleep(1);
		}
		return 0;
	}

    线程中id的讨论:


      tid                     线程地址空间首地址


      pcb-》pid               轻量级进程id


      pcb-》tgid                进程(线程组)id,默认等于首线程id

   -2.线程终止:
      return         不能在mian函数中使用return退出(return退出的时进程---导致所有线程退出)
      exit  phread _exit(void*retval);      退出线程自身,谁调用谁退出。retval:线程的退出返回值

      int pthread_cancel(phread _t thread);取消其他线程;让其他线程退出;thread:要取消的线程ID
       线程退出之后,默认不会自动释放资源,(保存自己的退出结果在线程独有的地址空间中);  因此也会造成
   资源泄露。
      主线程退出,其他线程依然可以正常运行
   -3.线程等待
     前提;一个线程创建出来。默认有一个属性叫joinable;处于joinable属性的线程退出后,不会自动释放资源;需要
被其他线程等待,才能释放资源;

处于joinable属性的线程必须被等待,否则造成资源泄露
    
    int pthread_Join(pthread_t thread,void**retval);
   功能:阻塞等待指定线程退出;通过retval获取返回值
  -4.线程分离;

     线程分离就是将线程joinable属性修改为detach属性
     线程若处于detach属性,则线程退出后将自动回收资源;
     并且这个线程不需要被等待,等待是毫无意义的,因为线程退出返回值占用的空间已经被回收了
     pthread_detach(pthread_t tid)
     线程分离的适用场景;对线程的返回值并不关心
     线程分离可以在任意线程中实现

4.线程安全;
   -1.概念;多个线程同时对临界资源进行访问而不会造成数据二义
   -2.如何实现线程安全;同步+互斥
        同步:对临界资源访问的时序合理性
        互斥:对临界资源同一时间访问的唯一性
        线程互斥的实现:互斥锁

     例子;黄牛抢票;

     代码示例;
       

int tick=100;//设置一百张票;
void *yellow_bull(void *arg)
{
      while(1)
      {
            if(ticket>0)
             {
                  printf("bull %d get a ticket:%d\n",(int)arg,ticket);
             
                  ticket--;

             }
             else
             {
                  printf("have no ticket, bull %d exit\n",(int)arg);
                  pthread_exit(NULL);
             }
       }
}

int main()
{
     pthread_t tid[4];
     int i;
     for(i=0;i<4;i++)
      {
            int ret=pthread_create(&tid[i],NULL,yellow_bull,(void*)i);
            if (ret!=0)
              {
                    printf("thread create error\n");
                    return -1;
              {
      }
      for(i=0;i<4;i++)
       {
        pthread_join(tid[i],NULL);
       }
    return 0;
}









  

 互斥锁:是一个计数器(0/1);
         1.定义一个互斥锁变量 pthread_mutex_t
         2.对互斥锁变量进行初始化  pthread_mutex_init(&mutex,&attr)
         3.对临界资源操作之前先加锁 pthread_mutex_lock(&mutex)
                若可以加锁则直接修改计数,函数返回;否则挂起等待
                pthread_mutex_trylock / pthread_mutex_timedlock
         4. 对临界资源操作完毕后进行解锁
                pthread_mutex_unlock(&mutex);
         5.销毁互斥锁;
                pthread_mutex_destroy(&mutex);
       

       死锁:多个线程对锁资源进行竞争访问,但是因为推进顺序不当,导致互相等待,是程序无法往下运行。(互相僵持)
       死锁产生的四个必要条件:
         1.互斥条件           一个锁只有一个线程可以获取
         2.不可剥夺条件    我加的锁别人不能解
         3.请求保持条件    拿着A锁,去请求B锁  ,但是获取不到B锁,也不释放A锁
         4.环路等待条件    我拿着A锁请求B锁,对方拿着B锁请求A锁        
       如何死锁预防?
       破坏四个必要条件
       
       死锁避免:死锁检测算法。银行家算法。


    线程间同步的实现:等待+唤醒
         操作 条件不满足则等待,别人促使条件满足后唤醒等待
         条件变量:
               条件变量实现同步:
                      线程在对临界资源访问之前,先判断是否能够操作;若可以操作则线程直接操作;
                      否则若不能操作;则条件变量提供等待功能;让pcb等待在队列上
                      其他线程促使操作条件满足,然后唤醒条件变量等待队列上的线程

           1.定义条件变量 pthread_cond_t cond
           2.条件变量初始化  pthread_cond_init(&cond,&attr);
           3.用户在判断条件不满足的情况下提供等待功能 pthread_cond_wait(&cond ,&mutex);
           4.用户在促使条件满足后,唤醒等待 pthread_cond_signal(&cond)/pthread_cond_broadcast(&cond)
           5.销毁条件变量pthread_cond_destroy(&cond)

       

你可能感兴趣的:(linux)