Python3 线程中常用的两个模块为:
- _thread(已经废弃)
- threading(推荐使用)
线程模块
Python3 通过两个标准库 _thread 和 threading 提供对线程的支持。
_thread 提供了低级别的、原始的线程以及一个简单的锁,它相比于 threading 模块的功能还是比较有限的。
threading 模块除了包含 _thread 模块中的所有方法外,还提供的其他方法:
- threading.currentThread(): 返回当前的线程变量。
- threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
- threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
除了使用方法外,线程模块同样提供了Thread类来处理线程,Thread类提供了以下方法:
- run(): 用以表示线程活动的方法。
- start():启动线程活动。
- join([time]): 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。
- isAlive(): 返回线程是否活动的。
- getName(): 返回线程名。
- setName(): 设置线程名。
使用 threading 模块创建线程
我们可以通过直接从 threading.Thread 继承创建一个新的子类,并实例化后调用 start() 方法启动新线程,即它调用了线程的 run() 方法:
import threading
import time
exitFlag = 0
class myThread (threading.Thread):
def __init__(self, threadID, name, counter):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.counter = counter
def run(self):
print ("开始线程:" + self.name)
print_time(self.name, self.counter, 5)
print ("退出线程:" + self.name)
def print_time(threadName, delay, counter):
while counter:
if exitFlag:
threadName.exit()
time.sleep(delay)
print ("%s: %s" % (threadName, time.ctime(time.time())))
counter -= 1
# 创建新线程
thread1 = myThread(1, "线程1", 1)
thread2 = myThread(2, "线程2", 2)
# 开启新线程
thread1.start()
thread2.start()
print ("退出主线程")
'''
开始线程:线程1
开始线程:线程2
退出主线程
线程1: Sat Mar 6 11:34:33 2021
线程2: Sat Mar 6 11:34:34 2021线程1: Sat Mar 6 11:34:34 2021
线程1: Sat Mar 6 11:34:35 2021
线程2: Sat Mar 6 11:34:36 2021
线程1: Sat Mar 6 11:34:36 2021
线程1: Sat Mar 6 11:34:37 2021
退出线程:线程1
线程2: Sat Mar 6 11:34:38 2021
线程2: Sat Mar 6 11:34:40 2021
线程2: Sat Mar 6 11:34:42 2021
退出线程:线程2
线程同步(多线程共享全局变量的问题)
如果多个线程共同对某个数据修改,则可能出现不可预料的结果,为了保证数据的正确性,需要对多个线程进行同步。
使用 Thread 对象的 Lock 和 Rlock 可以实现简单的线程同步,这两个对象都有 acquire 方法和 release 方法,对于那些需要每次只允许一个线程操作的数据,可以将其操作放到 acquire 和 release 方法之间。如下:
import threading
import time
class myThread (threading.Thread):
def __init__(self, threadID, name, counter):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.counter = counter
def run(self):
print ("开启线程: " + self.name)
# 获取锁,用于线程同步
threadLock.acquire()
print_time(self.name, self.counter, 3)
# 释放锁,开启下一个线程
threadLock.release()
def print_time(threadName, delay, counter):
while counter:
time.sleep(delay)
print ("%s: %s" % (threadName, time.ctime(time.time())))
counter -= 1
threadLock = threading.Lock()
threads = []
# 创建新线程
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
# 开启新线程
thread1.start()
thread2.start()
# 添加线程到线程列表
threads.append(thread1)
threads.append(thread2)
# 等待所有线程完成
for t in threads:
t.join()
print ("退出主线程")
参考链接:https://www.runoob.com/python3/python3-multithreading.html
Lock版本生产者和消费者模式:
生产者和消费者模式是多线程开发中经常见到的一种模式。生产者的线程专门用来生产一些数据,然后存放到一个中间的变量中。消费者再从这个中间的变量中取出数据进行消费。但是因为要使用中间变量,中间变量经常是一些全局变量,因此需要使用锁来保证数据完整性。以下是使用threading.Lock
锁实现的“生产者与消费者模式”的一个例子:
import threading
import random
import time
gMoney = 1000
gLock = threading.Lock()
# 记录生产者生产的次数,达到10次就不再生产
gTimes = 0
class Producer(threading.Thread):
def run(self):
global gMoney
global gLock
global gTimes
while True:
money = random.randint(100, 1000)
gLock.acquire()
# 如果已经达到10次了,就不再生产了
if gTimes >= 10:
gLock.release()
break
gMoney += money
print('%s当前存入%s元钱,剩余%s元钱' % (threading.current_thread(), money, gMoney))
gTimes += 1
time.sleep(0.5)
gLock.release()
class Consumer(threading.Thread):
def run(self):
global gMoney
global gLock
global gTimes
while True:
money = random.randint(100, 500)
gLock.acquire()
if gMoney > money:
gMoney -= money
print('%s当前取出%s元钱,剩余%s元钱' % (threading.current_thread(), money, gMoney))
time.sleep(0.5)
else:
# 如果钱不够了,有可能是已经超过了次数,这时候就判断一下
if gTimes >= 10:
gLock.release()
break
print("%s当前想取%s元钱,剩余%s元钱,不足!" % (threading.current_thread(),money,gMoney))
gLock.release()
def main():
for x in range(5):
Consumer(name='消费者线程%d'%x).start()
for x in range(5):
Producer(name='生产者线程%d'%x).start()
if __name__ == '__main__':
main()
Condition版的生产者与消费者模式:
Lock
版本的生产者与消费者模式可以正常的运行。但是存在一个不足,在消费者中,总是通过while True
死循环并且上锁的方式去判断钱够不够。上锁是一个很耗费CPU资源的行为。因此这种方式不是最好的。还有一种更好的方式便是使用threading.Condition
来实现。threading.Condition
可以在没有数据的时候处于阻塞等待状态。一旦有合适的数据了,还可以使用notify
相关的函数来通知其他处于等待状态的线程。这样就可以不用做一些无用的上锁和解锁的操作。可以提高程序的性能。首先对threading.Condition
相关的函数做个介绍,threading.Condition
类似threading.Lock
,可以在修改全局数据的时候进行上锁,也可以在修改完毕后进行解锁。以下将一些常用的函数做个简单的介绍:
acquire
:上锁。release
:解锁。wait
:将当前线程处于等待状态,并且会释放锁。可以被其他线程使用notify
和notify_all
函数唤醒。被唤醒后会继续等待上锁,上锁后继续执行下面的代码。notify
:通知某个正在等待的线程,默认是第1个等待的线程。notify_all
:通知所有正在等待的线程。notify
和notify_all
不会释放锁。并且需要在release
之前调用。
Condition
版的生产者与消费者模式代码如下:
import threading
import random
import time
gMoney = 1000
gCondition = threading.Condition()
gTimes = 0
gTotalTimes = 5
class Producer(threading.Thread):
def run(self):
global gMoney
global gCondition
global gTimes
while True:
money = random.randint(100, 1000)
gCondition.acquire()
if gTimes >= gTotalTimes:
gCondition.release()
print('当前生产者总共生产了%s次'%gTimes)
break
gMoney += money
print('%s当前存入%s元钱,剩余%s元钱' % (threading.current_thread(), money, gMoney))
gTimes += 1
time.sleep(0.5)
gCondition.notify_all()
gCondition.release()
class Consumer(threading.Thread):
def run(self):
global gMoney
global gCondition
while True:
money = random.randint(100, 500)
gCondition.acquire()
# 这里要给个while循环判断,因为等轮到这个线程的时候
# 条件有可能又不满足了
while gMoney < money:
if gTimes >= gTotalTimes:
gCondition.release()
return
print('%s准备取%s元钱,剩余%s元钱,不足!'%(threading.current_thread(),money,gMoney))
gCondition.wait()
gMoney -= money
print('%s当前取出%s元钱,剩余%s元钱' % (threading.current_thread(), money, gMoney))
time.sleep(0.5)
gCondition.release()
def main():
for x in range(5):
Consumer(name='消费者线程%d'%x).start()
for x in range(2):
Producer(name='生产者线程%d'%x).start()
if __name__ == '__main__':
main()
Queue线程安全队列:
在线程中,访问一些全局变量,加锁是一个经常的过程。如果你是想把一些数据存储到某个队列中,那么Python内置了一个线程安全的模块叫做queue
模块。Python中的queue模块中提供了同步的、线程安全的队列类,包括FIFO(先进先出)队列Queue,LIFO(后入先出)队列LifoQueue。这些队列都实现了锁原语(可以理解为原子操作,即要么不做,要么都做完),能够在多线程中直接使用。可以使用队列来实现线程间的同步。相关的函数如下:
- 初始化Queue(maxsize):创建一个先进先出的队列。
- qsize():返回队列的大小。
- empty():判断队列是否为空。
- full():判断队列是否满了。
- get():从队列中取最后一个数据。
- put():将一个数据放到队列中。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)