python基础语法23-多线程理论

一、概述
线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,一个进程可以运行多个线程。多线程类似于同时执行多个不同程序,多线程优点:
(1)使用线程可以把占据长时间的程序中的任务放到后台去处理。
(2)用户界面可以更加吸引人,比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度。
(3)程序的运行速度可能加快。
(4)在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下我们可以释放一些珍贵的资源如内存占用等等。
每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
每个线程都有他自己的一组CPU寄存器,称为线程的上下文,该上下文反映了线程上次运行该线程的CPU寄存器的状态。
指令指针和堆栈指针寄存器是线程上下文中两个最重要的寄存器,线程总是在进程得到上下文中运行的,这些地址都用于标志拥有线程的进程地址空间中的内存。
线程可以被抢占(中断)。
在其他线程正在运行时,线程可以暂时搁置(也称为睡眠) -- 这就是线程的退让。
线程可以分为:
内核线程:由操作系统内核创建和撤销。
用户线程:不需要内核支持而在用户程序中实现的线程。
Python3线程中常用的两个模块为:
_thread
threading(推荐使用)
二、threading介绍
1.threading函数
通常情况下,Python程序启动时,Python解释器会启动一个继承自threading.Thread的threading._MainThread线程对象作为主线程,所以涉及到threading.Thread的方法和函数时通常都算上了这个主线程的,比如在启动程序时打印threading.active_count()的结果就已经是1了。
threading.active_count():返回当前存活的 Thread对象的数量。返回值与 enumerate()所返回的列表长度一致。
threading.current_thread():返回当前对应调用者的控制线程的 Thread对象。如果当前调用者控制的线程不是通过threading.Thread创建的,则返回一个功能受限的虚拟线程对象。
threading.get_ident():返回当前线程的线程标识符。注意当一个线程退出时,它的线程标识符可能会被之后新创建的线程复用。
threading.enumerate():返回当前存活的Thread线程对象列表。
threading.main_thread():返回主线程对象,通常情况下,就是程序启动时Python解释器创建的threading._MainThread线程对象。
threading.stack_size([size]):返回创建线程时使用的堆栈大小。
threading.settrace(func):为所有 threading 模块开始的线程设置追踪函数。
threading.setprofile(func):为所有 threading 模块开始的线程设置性能测试函数
2.threading常量
threading.TIMEOUT_MAX:阻塞函数( Lock.acquire(), RLock.acquire(), Condition.wait(), ...)中形参 timeout 允许的最大值
当线程对象一但被创建,其活动一定会因调用线程的 start()方法开始。这会在独立的控制线程调用 run()方法。
3.threading对象
threading.Thread目前还没有优先级和线程组的功能,而且创建的线程也不能被销毁、停止、暂定、恢复或中断。
threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)
如果这个类的初始化方法被重写,请确保在重写的初始化方法中做任何事之前先调用threading.Thread类的__init__方法。
参数:
group:应该设为None,即不用设置,使用默认值就好,因为这个参数是为了以后实现ThreadGroup类而保留的。
target:在run方法中调用的可调用对象,即需要开启线程的可调用对象,比如函数或方法。
name:线程名称,默认为“Thread-N”形式的名称,N为较小的十进制数。
args:在参数target中传入的可调用对象的参数元组,默认为空元组()。
kwargs:在参数target中传入的可调用对象的关键字参数字典,默认为空字典{}。
daemon:默认为None,即继承当前调用者线程(即开启线程的线程,一般就是主线程)的守护模式属性,如果不为None,则无论该线程是否为守护模式,都会被设置为“守护模式”。
方法:
start():开启线程活动。它将使得run()方法在一个独立的控制线程中被调用,需要注意的是同一个线程对象的start()方法只能被调用一次,如果调用多次,则会报RuntimeError错误。
run():此方法代表线程活动。
join(timeout=None):让当前调用者线程(即开启线程的线程,一般就是主线程)等待,直到线程结束(无论它是什么原因结束的),timeout参数是以秒为单位的浮点数,用于设置操作超时的时间,返回值为None。如果想要判断线程是否超时,只能通过线程的is_alive方法来进行判断。join方法可以被调用多次。如果对当前线程使用join方法(即线程在内部调用自己的join方法),或者在线程没有开始前使用join方法,都会报RuntimeError错误。
name:线程的名称字符串,并没有什么实际含义,多个线程可以赋予相同的名称,初始值由初始化方法来设置。
ident:线程的标识符,如果线程还没有启动,则为None。ident是一个非零整数,参见threading.get_ident()函数。当线程结束后,它的ident可能被其他新创建的线程复用,当然就算该线程结束了,它的ident依旧是可用的。
is_alive():线程是否存活,返回True或者False。在线程的run()运行之后直到run()结束,该方法返回True。
daemon:表示该线程是否是守护线程,True或者False。设置一个线程的daemon必须在线程的start()方法之前,否则会报RuntimeError错误。这个值默认继承自创建它的线程,主线程默认是非守护线程的,所以在主线程中创建的线程默认都是非守护线程的,即daemon=False。
isDaemon()/setDaemon():旧的 daemon 取值/设值 API;请改为直接以特性属性方式使用它。
4.锁对象threading.Lock
threading.Lock是直接通过_thread模块扩展实现的。当锁在被锁定时,它并不属于某一个特定的线程。
锁只有"锁定"和"非锁定"两种状态,当锁被创建时,是处于"非锁定"状态的。当锁已经被锁定时,再次调用acquire()方法会被阻塞执行,直到锁被调用release()方法释放掉锁并将其状态改为"非锁定"。
同一个线程获取锁后,如果在释放锁之前再次获取锁会导致当前线程阻塞,除非有另外的线程来释放锁,如果只有一个线程,并且发生了这种情况,会导致这个线程一直阻塞下去,即形成了死锁。所以在获取锁时需要保证锁已经被释放掉了,或者使用递归锁来解决这种情况。
acquire(blocking=True, timeout=-1) 可以阻塞或非阻塞地获得锁。
release() 释放一个锁
locked() 如果获得了锁则返回真值
5.递归锁对象threading.RLock
递归锁和普通锁的差别在于加入了"所属线程"和"递归等级"的概念,释放锁必须有获取锁的线程来进行释放,同时,同一个线程在释放锁之前再次获取锁将不会阻塞当前线程,只是在锁的递归等级上加了1(获得锁时的初始递归等级为1)。
使用普通锁时,对于一些可能造成死锁的情况,可以考虑使用递归锁来解决。
acquire(blocking=True, timeout=-1) 可以阻塞或非阻塞地获得锁
release() 释放锁,自减递归等级
6.条件变量对象threading.Condition
条件变量总是与某种类型的锁对象相关联,锁对象可以通过传入获得,或者在缺省的情况下自动创建。当多个条件变量需要共享同一个锁时,传入一个锁很有用。锁是条件对象的一部分,你不必单独地跟踪它。
threading.Condition(lock=None):一个条件变量对象允许一个或多个线程等待,直到被另一个线程通知。lock参数必须是一个Lock对象或者RLock对象,并且会作为底层锁使用,默认使用RLock。
acquire(*args) 请求底层锁。此方法调用底层锁的相应方法,返回值是底层锁相应方法的返回值。
release() 释放底层锁。此方法调用底层锁的相应方法。没有返回值。
wait(timeout=None) 等待直到被通知或发生超时。如果线程在调用此方法时没有获得锁,将会引发 RuntimeError 异常。
wait_for(predicate, timeout=None) 等待,直到条件计算为真。 predicate 应该是一个可调用对象而且它的返回值可被解释为一个布尔值。可以提供 timeout 参数给出最大等待时间。
notify(n=1) 默认唤醒一个等待这个条件的线程。如果调用线程在没有获得锁的情况下调用这个方法,会引发 RuntimeError 异常。
notify_all() 唤醒所有正在等待这个条件的线程。这个方法行为与 notify() 相似,但并不只唤醒单一线程,而是唤醒所有等待线程。如果调用线程在调用这个方法时没有获得锁,会引发 RuntimeError 异常。
7.信号量对象threading.Semaphore
这是计算机科学史上最古老的同步原语之一,早期的荷兰科学家 Edsger W. Dijkstra 发明了它。(他使用名称 P() 和 V() 而不是 acquire() 和 release() )。
一个信号量管理一个内部计数器,该计数器因 acquire() 方法的调用而递减,因 release() 方法的调用而递增。 计数器的值永远不会小于零;当 acquire() 方法发现计数器为零时,将会阻塞,直到其它线程调用 release() 方法。
threading.Semaphore(value=1):value参数默认值为1,如果指定的值小于0,则会报ValueError错误。一个信号量对象管理一个原子性的计数器,代表release()方法调用的次数减去acquire()方法的调用次数,再加上一个初始值。
acquire(blocking=True, timeout=None) 获取一个信号量
release() 释放一个信号量,将内部计数器的值增加1。
8.事件对象threading.Event
一个事件对象管理一个内部标志,初始状态默认为False,set()方法可将它设置为True,clear()方法可将它设置为False,wait()方法将线程阻塞直到内部标志的值为True。
如果一个或多个线程需要知道另一个线程的某个状态才能进行下一步的操作,就可以使用线程的event事件对象来处理。
is_set():当内部标志为True时返回True。
set():设置内部标志为True。此时所有等待中的线程将被唤醒,调用wait()方法的线程将不会被阻塞。
clear():将内部标志设置为False。所有调用wait()方法的线程将被阻塞,直到调用set()方法将内部标志设置为True。
wait(timeout=None):阻塞线程直到内部标志为True,或者发生超时事件。如果调用时内部标志就是True,那么不会被阻塞,否则将被阻塞。timeout为浮点类型的秒数。
9.定时器对象threading.Timer
表示一个操作需要在等待一定时间之后执行,相当于一个定时器。Timer类是threading.Thread的子类,所以它可以像一个自定义线程一样工作。
和线程一样,可以通过start()方法启动定时器,在定时器计时结束之前(线程开启之前)可以使用cancel()方法停止计时器。计时器等待的时间可能与用户设置的时间不完全一样。
threading.Timer(interval, function, args=None, kwargs=None)
参数含义:
interval:间隔时间,即定时器秒数。
function:执行的函数。
args:传入function的参数,如果为None,则会传入一个空列表。
kwargs:传入function的关键字参数,如果为None,则会传入一个空字典。
cancel():停止计时器,并取消对应函数的执行,这个方法只有在计时器还没有计时结束之前才会生效,如果已经开始执行函数,则不会生效。
10.栅栏对象threading.Barrier
栅栏对象用于处理一批指定数量的线程,而这些线程需要等待彼此的状态。这些线程中的每个线程都会尝试调用wait()方法,然后阻塞,直到所有线程都调用了wait()方法,然后所有线程会被同时释放。
threading.Barrier(parties, action=None, timeout=None)
参数含义:
parties:指定需要创建的栅栏对象的线程数。
action:一个可调用对象,当所有线程都被释放时,在释放前,其中一个线程(随机)会自动调用这个对象。
timeout:设置wait()方法的超时时间。
方法:
wait(timeout=None):通过栅栏
reset():将一个栅栏对象重置为默认的初始态。
abort():使栅栏对象进入破损状态。
parties:通过栅栏的线程数量。
n_waiting:在栅栏中正在等待的线程数量。
broken:如果栅栏对象为破损状态则返回True。
11.线程优先级队列Queue
Python的 Queue模块中提供了同步的、线程安全的队列类,包括FIFO(先入先出)队列Queue,LIFO(后入先出)队列LifoQueue,和优先级队列 PriorityQueue。
这些队列都实现了锁原语,能够在多线程中直接使用,可以使用队列来实现线程间的同步。
Queue模块中的常用方法:
Queue.qsize() 返回队列的大小
Queue.empty() 如果队列为空,返回True,反之False
Queue.full() 如果队列满了,返回True,反之False
Queue.full 与 maxsize 大小对应
Queue.get([block[, timeout]])获取队列,timeout等待时间
Queue.get_nowait() 相当Queue.get(False)
Queue.put(item) 写入队列,timeout等待时间
Queue.put_nowait(item) 相当Queue.put(item, False)
Queue.task_done() 在完成一项工作之后,Queue.task_done()函数向任务已经完成的队列发送一个信号
Queue.join() 实际上意味着等到队列为空,再执行别的操作

你可能感兴趣的:(python,python,java,开发语言,threading)