本文主要介绍Python线程解释。本文详细讲解了线程的各个方面的知识,比如线程基础知识,线程状态,线程同步(锁),线程通信(条件变量)等等。有需要的可以参考一下。
1. 线程基础
1.1. 线程状态
线程有五种状态,状态转换的过程如下图所示:
1.2. 线程同步(锁)
多线程的好处是可以同时运行多个任务(至少感觉是这样)。但是,当线程需要共享数据时,可能会出现数据不同步的情况。考虑一个情况,一个列表中所有元素都是0,线程‘set’从后向前把所有元素都改成1,线程‘print’负责从前向后读取列表并打印。然后,可能当线程' set '开始变化时,线程' print '会打印列表,输出变成一半0一半1,这就是数据不同步。为了避免这种情况,引入了锁的概念。
锁有两种状态:3354锁定和解锁。每当像“set”这样的线程想要访问共享数据时,它必须首先获得一个锁;如果另一个线程,比如' print ',已经被锁定,那么就让线程' set '暂停,也就是同步阻塞;等到线程“print”访问并释放锁,然后让线程“set”继续。经过这样的处理,在打印列表时,要么输出全0,要么输出全1,不会再出现半0半1的尴尬场景。
下图显示了线程和锁之间的交互:
1.3. 线程通信(条件变量)
但是,还有一个尴尬的情况:名单一开始就不存在;而是由线程“create”创建的。如果“设置”或“打印”在“创建”运行之前访问列表,将出现异常。使用锁可以解决这个问题,但是“设置”和“打印”将需要一个无限循环3354。他们不知道“创建”将在何时运行。运行后让'创建'通知'设置'和'打印'显然是更好的解决方案。因此,引入了条件变量。
条件变量允许“set”和“print”等线程在不满足条件时等待(当列表为None时)。当条件满足时(列表已创建),将发送通知告诉“设置”和“打印”条件已满足。你该起床工作了。然后“设置”和“打印”将继续运行。
与线程条件变量的交互如下图所示:
1.4.线程运行和阻塞的状态转换
最后,看看线程运行和阻塞状态之间的转换。
拥塞有三种情况:
同步阻塞指的是竞争锁定的状态。当一个线程请求锁定时,就会进入这种状态,一旦成功获得锁定,就会回到运行状态。
等待是指等待其他线程通知的状态。线程获得条件锁定后,调用“Wait”就会进入这种状态。一旦其他线程给出通知,线程将进入同步阻塞状态,再次竞争条件锁定;
其他阻塞是指调用time.sleep()、anotherthread.join()或等待IO时的阻塞。在这种状态下,线程不会释放获取的锁。
温馨提示:如果你能理解这些内容,下一个题目就很容易了;而且,这些内容在大多数流行的编程语言中都是一样的。(意思是你得看懂_我觉得作者水平低,得看懂别人的教程)
2. thread
Python通过两个标准库thread和threading提供对线程的支持。Thread提供了一个低级的、原始的线程和一个简单的锁。
复制代码如下:
#编码:UTF-8
导入线程
导入时间
#在线程中执行的函数
定义函数():
对于范围(5)中的I:
打印'功能'
时间.睡眠(1)
#结束当前线程
#此方法等效于thread.exit_thread()
Thread.exit() #当func返回时,线程也将结束。
#启动一个线程,该线程立即开始运行
#此方法等效于thread.start_new_thread()
#第一个参数是方法,第二个参数是方法的参数。
Thread.start_new(func,())#方法没有参数时需要传入空元组。
#创建锁(LockType,不能直接实例化)
#此方法等效于thread.allocate_lock()
lock=thread.allocate()
#确定锁是锁定的还是释放的。
print lock.locked()
#锁通常用于控制对共享资源的访问
计数=0
#获取锁,成功获取锁后返回True。
#如果没有填充可选的超时参数,它将一直阻塞,直到获得锁。
#否则,超时后将返回False
if lock.acquire():
计数=1
#打开锁
lock.release()
# thread模块提供的线程将在主线程结束后同时结束。
时间.睡眠(6)
thread 模块提供的其他方法:
Thread.interrupt_main():在其他线程中终止主线程。
Thread.get_ident():获取表示当前线程的幻数,通常用于从字典中获取线程相关的数据。这个数字本身没有意义,在线程结束时会被新的线程重用。
Thread还提供了一个ThreadLocal类来管理与线程相关的数据,名为thread。_local,在线程中引用。
由于thread提供的线程函数不多,主线程结束后无法继续运行,不提供条件变量等原因,一般不使用Thread模块,这里就不介绍了。
3. threading
基于Java的线程模型的线程设计。锁和条件变量是Java中对象的基本行为(每个对象都有自己的锁和条件变量),而在Python中它们是独立的对象。Python线程提供了Java线程行为的子集;如果没有优先级和线程组,线程就不能被停止、挂起、恢复或中断。由Python在Java线程中部分实现的静态方法在线程中作为模块方法提供。
threading 模块提供的常用方法:
Threading.currentThread():返回当前线程变量。
Threading.enumerate():返回包含正在运行的线程的列表。运行是指开始后结束前的线程,不包括开始前和结束后的线程。
Threading.activeCount():返回正在运行的线程数,结果与len(threading.enumerate())相同。
threading模块提供的类:
线程、锁、Rlock、条件、[有界]信号量、事件、计时器、局部。
3.1. Thread
Thread是一个线程类,类似于Java。有两种方法可以使用它,要么直接传入要运行的方法,要么从Thread继承并覆盖run():
复制代码如下:
#编码:UTF-8
导入线程
#方法1:将要执行的方法作为参数传递给Thread的构造函数
定义函数():
打印“func()传递给线程”
t=螺纹。线程(target=func)
启动()
#方法2:从线程继承,重写run()
类MyThread(线程。线程):
定义运行(自身):
打印'从线程扩展的MyThread '
t=MyThread()
启动()
构造方法:
Thread(group=None,target=None,name=None,args=(),kwargs={})
Group:线程组,还没有实现。库引用中的提示必须是None;
目标:要执行的方法;
名称:线程名称;
Args/kwargs:要传递给方法的参数。
实例方法:
IsAlive():返回线程是否正在运行。运行是指启动后和终止前。
Get/setName(name):获取/设置线程名称。
Is/setDaemon(bool):获取/设置是否守护线程。初始值是从创建线程的线程继承的。当没有非守护线程仍在运行时,程序将终止。
Start():启动线程。
Join([timeout]):阻塞当前上下文的线程,直到调用此方法的线程终止或达到指定的超时(可选参数)。
使用join()的示例:
复制代码如下:
#编码:UTF-8
导入线程
导入时间
定义上下文(tJoin):
打印'在threadContext中'
tJoin.start()
# t上下文将被阻止,直到threadJoin终止。
tJoin.join()
# tJoin被终止并继续执行。
打印'输出线程上下文'
定义联接():
在threadJoin中打印
时间.睡眠(1)
打印出“螺纹接头”
tJoin=线程。线程(目标=连接)
tContext=线程。线程(target=context,args=(tJoin,))
tContext.start()
运行结果:
复制代码如下:
在threadContext中。
在threadJoin中。
out threadJoin
out threadContext
3.2. Lock
Lock(指令锁定)是可用的最低同步指令。当锁被锁定时,它不属于特定的线程。Lock包含两种状态,——锁定和解锁,以及两种基本方法。
可以认为Lock有一个锁池。当线程请求锁时,它将线程放入池中,直到它获得锁,然后离开池。在状态图中,池中的线程处于同步阻塞状态。
构造方法:
锁定()
实例方法:
Acquire([timeout]):将线程置于同步阻塞状态,并尝试获取锁。
Release():释放锁。线程必须在使用前被锁定,否则将引发异常。
复制代码如下:
#编码:UTF-8
导入线程
导入时间
数据=0
锁=线程。锁定()
定义函数():
全局数据
打印“%s获取锁.”% threading.currentThread()。getName()
#当调用acquire([timeout])时,线程将总是阻塞,
#直到获得锁或直到超时后秒(timeout参数是可选的)。
#返回是否获取了锁。
if lock.acquire():
打印“%s获取锁”% threading.currentThread()。getName()
数据=1
时间.睡眠(2)
打印“%s释放锁.”% threading.currentThread()。getName()
#调用release()将释放锁。
lock.release()
t1=线程。线程(target=func)
t2=线程。线程(target=func)
t3=线程。线程(target=func)
t1.start()
t2.start()
t3.start()
3.3. RLock
RLock(可重入锁)是一个同步指令,可以被同一个线程多次请求。RLock使用了“拥有线程”和“递归层次”的概念。当它被锁定时,RLock由一个线程拥有。带有RLock的线程可以再次调用acquire(),释放锁时需要调用release()相同的次数。
可以认为RLock包含一个锁池和一个初始值为0的计数器。每次成功调用acquire()/release()时,计数器会为1/-1,为0时,锁被解锁。
构造方法:
RLock()
实例方法:
acquire([超时])/release():类似于Lock。
复制代码如下:
#编码:UTF-8
导入线程
导入时间
rlock=线程。RLock()
定义函数():
#第一次请求锁定
打印“%s获取锁.”% threading.currentThread()。getName()
if rlock.acquire():
打印“%s获取锁”% threading.currentThread()。getName()
时间.睡眠(2)
#第二次请求锁定
打印“%s再次获取锁定.”% threading.currentThread()。getName()
if rlock.acquire():
打印“%s获取锁”% threading.currentThread()。getName()
时间.睡眠(2)
#第一次释放锁
打印“%s释放锁.”% threading.currentThread()。getName()
rlock.release()
时间.睡眠(2)
#第二次释放锁
打印“%s释放锁.”% threading.currentThread()。getName()
rlock.release()
t1=线程。线程(target=func)
t2=线程。线程(target=func)
t3=线程。线程(target=func)
t1.start()
t2.start()
t3.start()
3.4. Condition
条件通常与锁相关联。当需要在多个Contidion之间共享一个锁时,可以将一个Lock/RLock实例传递给构造函数,否则它会自己生成一个RLock实例。
可以认为,除了带锁的锁池,该条件还包含一个等待池,池中的线程在状态图中处于等待阻塞状态,直到另一个线程调用notify()/notifyAll()通知;收到通知后,线程进入锁池并等待锁。
构造方法:
条件([锁定/锁])
实例方法:
Acquire([timeout])/release():调用关联锁的对应方法。
Wait([timeout]):调用此方法将使线程进入等待条件池,等待通知并释放锁。线程必须在使用前被锁定,否则将引发异常。
Notify():调用这个方法会从等待池中挑选一个线程并通知它,收到通知的线程会自动调用acquire()尝试获取锁(进入锁池);其他线程仍在池中等待。调用此方法不会释放锁。线程必须在使用前被锁定,否则将引发异常。
NotifyAll():调用这个方法将通知等待池中的所有线程,所有这些线程将进入锁定池尝试获取锁。调用此方法不会释放锁。线程必须在使用前被锁定,否则将引发异常。
例子是非常常见的生产者/消费者模型:
复制代码如下:
#编码:UTF-8
导入线程
导入时间
#商品
产品=无
#条件变量
con=线程。条件()
#生产者方法
定义生产():
全球产品
if con.acquire():
虽然正确:
如果产品是无:
打印“生产.”字样
product='任何东西'
#告知消费者商品已经生产
con.notify()
#等待通知
con.wait()
时间.睡眠(2)
#消费者方法
定义消费():
全球产品
if con.acquire():
虽然正确:
如果产品不是无:
打印“消费.”
产品=无
#通知生产商货物已经没有了
con.notify()
#等待通知
con.wait()
时间.睡眠(2)
t1=线程。线程(目标=产生)
t2=线程。线程(目标=消费)
t2.start()
t1.start()
3.5. Semaphore/BoundedSemaphore
sehore(Semaphore)是计算机科学史上最古老的同步指令之一。Semaphore管理一个内置的计数器,每当调用acquire()时该计数器为-1,当调用release()时该计数器为1。计数器不能小于0;当计数器为0时,acquire()会将线程阻塞到同步锁定状态,直到其他线程调用release()。
基于这一特性,信号量经常被用来同步一些具有“来宾天花板”的对象,比如连接池。
BoundedSemaphore和Semaphore唯一的区别是,前者在调用release()时会检查计数器的值是否超过了计数器的初始值,如果超过了就会抛出异常。
构造方法:
信号量(value=1): value=1): Value是计数器的初始值。
实例方法:
获取([超时]):请求一个信号量。如果计数器为0,线程将被阻塞到同步阻塞状态;否则,计数器将为-1并立即返回。
Release():释放信号量,将计数器设置为1,如果使用了BoundedSemaphore,则检查释放的次数。release()方法不检查线程是否获得了信号量。
复制代码如下:
#编码:UTF-8
导入线程
导入时间
#计数器的初始值是2
信号量=线程。信号量(2)
定义函数():
#请求信号量,成功后计数器为-1;计数器为0时阻塞
打印“%s获取信号量.”% threading.currentThread()。getName()
if semaphore.acquire():
print“% s get semaphore“% threading . current thread()。getName()
时间.睡眠(4)
#释放信号量,计数器1
打印“%s”释放信号量“% threading.currentThread()。getName()
信号量. release()
t1=线程。线程(target=func)
t2=线程。线程(target=func)
t3=线程。线程(target=func)
t4=线程。线程(target=func)
t1.start()
t2.start()
t3.start()
t4.start()
时间.睡眠(2)
#没有得到信号量的主线程也可以调用release。
#如果使用了BoundedSemaphore,t4将在释放信号量时抛出异常。
打印“不获取主线程释放信号量”
信号量. release()
3.6. Event
事件是最简单的线程通信机制之一:一个线程通知事件,而其他线程等待事件。事件有一个内置的初始False标志,调用set()时设置为True,调用clear()时重置为False。Wait()将阻塞线程以等待阻塞状态。
事件实际上是条件的简化版本。没有事件锁,所以线程不能进入同步阻塞状态。
构造方法:
事件()
实例方法:
IsSet():当内置标志为真时返回真。
Set():将标志设置为True,通知所有处于等待阻塞状态的线程恢复运行。
Clear():将标志设置为False。
Wait([timeout]):如果标志为真,则立即返回;否则,该线程将被阻塞,以等待其他线程调用set()。
复制代码如下:
#编码:UTF-8
导入线程
导入时间
事件=线程。事件()
定义函数():
#等待事件,进入等待阻塞状态。
打印“%s等待事件.”% threading.currentThread()。getName()
event.wait()
#收到事件后进入运行状态。
打印“%s接收事件”% threading.currentThread()。getName()
t1=线程。线程(target=func)
t2=线程。线程(target=func)
t1.start()
t2.start()
时间.睡眠(2)
#发送事件通知
打印“主线程设置事件”
event.set()
3.7. Timer
Timer(定时器)是Thread的派生类,用于在指定时间后调用一个方法。
构造方法:
计时器(interval,function,args=[],kwargs={})
间隔:指定的时间
函数:要执行的方法
args/kwargs:方法的参数
实例方法:
计时器从线派生,没有增加实例方法。
复制代码代码如下:
#编码:UTF-8
导入线程
定义函数():
打印“你好定时器!”
定时器=线程。定时器(5,func)
timer.start()
3.8. local
当地的是一个小写字母开头的类,用于管理线程本地(线程局部的)数据。对于同一个本地,线程无法访问其他线程设置的属性;线程设置的属性不会被其他线程设置的同名属性替换。
可以把当地的看成是一个"线程-属性字典"的字典,本地封装了从自身使用线程作为键检索对应的属性字典、再使用属性名作为键检索属性值的细节。
复制代码代码如下:
#编码:UTF-8
导入线程
local=threading.local()
local.tname='main '
定义函数():
local.tname='notmain '
打印本地. tname
t1=线程。线程(目标=函数)
t1.start()
t1.join()
打印本地. tname
熟练掌握线程、锁、条件就可以应对绝大多数需要使用线程的场合,某些情况下当地的也是非常有用的东西。本文的最后使用这几个类展示线程基础中提到的场景:
复制代码代码如下:
#编码:UTF-8
导入线程
列表=无
条件=线程。条件()
def doSet():
if condition.acquire():
而列表是空的:
condition.wait()
对于范围内的I(len(list))[:-1]:
列表[i]=1
条件。释放()
def doPrint():
if condition.acquire():
而列表是空的:
condition.wait()
因为我列出了:
打印我,
打印
条件。释放()
def doCreate():
全球列表
if condition.acquire():
如果列表为无:
列表=[范围(10)中的我为0]
condition.notifyAll()
条件。释放()
tset=线程。线程(target=doSet,name='tset ')
tprint=线程。线程(target=doPrint,name='tprint ')
tcreate=线程。线程(target=doCreate,name='tcreate ')
tset.start()
tprint.start()
tcreate.start()
全文完
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。