学无止境

少年辛苦终身事,莫向光阴惰寸功。——唐·杜荀鹤《题弟侄书堂》


Liunx编程-python线程

多线程


线程的定义

  • 进程是系统进行资源分配和调度的一个独立单位,进程是资源分配的单位,线程是cpu调度的单位。
  • .线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.
  • 线程包含再进程中

进程和线程的关系

  • 进程中第一个线程是主线程,主线程可以创建其他线程;其他线程也可以创建线程,线程之间是平等的;进程有父进程和子进程,独立的内存空间,唯一的标识符:pid。
  • python的thread模块是比较底层的模块,python的threading模块是对thread做了一些包装的,可以更加方便的创建线程.

使用threading模块的Thread创建线程


  • 单线程执行–主线程中执行

  • from threading import Thread
  • t1 = Thread(target=play,args=(“play”,))
  • 只要子线程还没有执行完任务,主线程就不会销毁
  • Thread类说明

    1. 可以明显看出使用了多线程并发的操作,花费时间要短很多。
    1. 创建好的线程,需要调用start()方法来启动。
    1. 在不同子线程中打印当前进程都是同一个进程,父进程也是同一个。
    1. 主线程会等待所有的子线程结束后才结束;也可以使用join()实现让其他线程(没有调用start的显示)和主线程等待当前子线程执行完毕,才往下执行。
  • 使用 threading.enumerate()查看线程数量


自定义threading.Thread子类实现多线程


  • 自定义的线程,注意:
  • 1.自定义的线程类必须继承Thread
  • 2.如果有__init__方法,注意调用:super().__ init__()
  • 3.必须重写run方法,因为只要start()默认调用run()
  • 不传 name 默认就有自动生成名字:Thread-1Thread -2..
  • 如果不想使用默认的名字则setName
  • 两种设置线程名字的方式
  • 1.再初始化的时候船体参数设置
  • 2.通过set Name设置
  • .threading.currentThread(): 获取线程名字
  • threading.enumerate(): 返回一个包含正在运行的线程的数量【list】。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
  • threading.activeCount():返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
  • Thread(group=None, target=None, name=None, args=(), kwargs={})
  • group: 线程组,目前还没有实现,库引用中提示必须是None
  • target: 要执行的方法;
  •   name: 线程名;
  •   args/kwargs: 要传入方法的参数。
  •  get/setName(name):获取/设置线程名。
  •  

  • 多线程之间共享全局变量


  • 全局变量线程共享

多线程安全和同步的概念


  • 多线程线程安全问题

  • 操作功效数据的代码是由多句组成
  • 拿到执行权,从停留的地方开始执行进而另一个线程抢到执行权,然后从另一个线程手里接过结果A:1-60;B:1-100;
  • A:60-120_—–>B:120-…..
  • 两个线程共享一个数量
  • 由于GIL(全局解释器)的存在我们要把测试的数据设置的大一点才能够使原来的GIL的锁不管用
  • threading.current_thread()
  • 获取当前线程的名字
  • t_name=thread.getName()
  • 解决线程安全问题:
  • 1.控制执行顺序

  • 互斥锁


  • 互斥锁概念

  • 1.当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制。
  • 线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁
  • 互斥锁为资源引入一个状态:锁定/非锁定。
  • 4.某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。
  • threading模块中定义了Lock类,可以方便的处理锁定:
  • 其中,锁定方法acquire[əˈkwaɪə(r)]可以有一个blocking参数。
  • 如果设定blocking为True,则当前线程会堵塞,直到获取到这个锁为止(如果没有指定,那么默认为True)
  • 如果设定blocking为False,则当前线程不会堵塞
  • 使用互斥锁解决线程安全问题

  • 创建互斥锁
  • from threading import Lock
  • 创建:mutex=Lock()
    
  • 上锁mutex.acquire()
    
  • 释放锁mutex.release()
    
  • 好处:
  • 确保了某段关键代码只能由一个线程从头到尾完整的执行
  • 坏处:
  • 组织了多线程的并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大的下降了,由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方持有的锁时,可能会造成死锁。

多进程之间非共享局部空间的数据


  • 多线程中类属性是共享的
  • 而实例属性是非共享的

  • #死锁


线程同步的综合应用


  • 多个线程同步

  • • 可以使用互斥锁完成多个任务,有序的进行工作,这就是线程的同步

生产者与消费者模式


from queue import Queue

  • Queue—-FIFO先进先出
  • Stack—-FILO/LIFO

threading.local()


  • 在多线程环境下,每个线程都有自己的数据。一个线程使用自己的局部变量比使用全局变量好,因为局部变量只有线程自己能看见,不会影响其他线程,而全局变量的修改必须加锁
  • 使用函数传参的方法-传递参数

  • 使用全局字典的方法-传递参数

  • 使用全局threading.local()的方法

  • 创建全局THreadlocal对象
  • local_school=threading.local()
  • 一个threading.local()变量虽然是全局变量,但每个线程都只能读写自己线程的独立副本,互不干扰。threading.local()解决了参数在一个线程中各个函数之间互相传递的问题

  • 同步和异步


  • 同步就是阻塞
  • 异步就是非阻塞

  • GIL问题


  • 单核单线程死循环,占满CPU