Liunx编程-python线程
多线程
线程的定义
- 进程是系统进行资源分配和调度的一个独立单位,进程是资源分配的单位,线程是cpu调度的单位。
- .线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.
- 线程包含再进程中
进程和线程的关系
- 进程中第一个线程是主线程,主线程可以创建其他线程;其他线程也可以创建线程,线程之间是平等的;进程有父进程和子进程,独立的内存空间,唯一的标识符:pid。
- python的thread模块是比较底层的模块,python的threading模块是对thread做了一些包装的,可以更加方便的创建线程.
使用threading模块的Thread创建线程
-
单线程执行–主线程中执行
- from threading import Thread
- t1 = Thread(target=play,args=(“play”,))
- 只要子线程还没有执行完任务,主线程就不会销毁
-
Thread类说明
-
- 可以明显看出使用了多线程并发的操作,花费时间要短很多。
-
- 创建好的线程,需要调用start()方法来启动。
-
- 在不同子线程中打印当前进程都是同一个进程,父进程也是同一个。
-
- 主线程会等待所有的子线程结束后才结束;也可以使用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