python关于多线程--threading模块

本文详细介绍了Python的threading模块,包括线程的创建、使用with语法、线程函数、常量及线程对象的属性和方法。强调了线程的daemon属性,以及线程的start()、run()、join()方法。同时讲解了线程锁对象threading.Lock的acquire()和release()方法,以及如何避免死锁。内容适合Python初学者和进阶开发者了解多线程编程。
摘要由CSDN通过智能技术生成

简介

threading模块中包含对多线程的多种操作,在python旧版本中使用thread模块 ,2.7和3 以后引入threading模块。threading相对于thread,对线程的操作更加丰富,个人推荐使用threading模块

一、with语法

是从Python2.5引入的一个新的语法,它是一种上下文管理协议,目的在于从流程图中把 try,except 和finally 关键字和
资源分配释放相关代码统统去掉,简化try….except….finlally的处理流程。

with some_lock:
	# .....
	# .....
	# do something
	pass

相当于:

some_lock.acquire()
try:
	# do something
	pass
finally:
	some_lock.release()

二、threading函数

在python3中方法名和函数名统一成了以小写字母加下划线的命令方式,但是Python2.x中threading模块的某些以驼峰命名的方法和函数仍然可用,如threading.active_count()和threading.activeCount()是一样的。
通常情况下,Python程序启动时,Python解释器会启动一个继承自threading.Thread的threading._MainThread线程对象作为主线程,所以涉及到threading.Thread的方法和函数时通常都算上了这个主线程的,比如在启动程序时打印threading.active_count()的结果就已经是1了。

threading.active_count()返回当前存活的threading.Thread线程对象数量,等同于len(threading.enumerate())。
threading.current_thread()返回此函数的调用者控制的threading.Thread线程对象。如果当前调用者控制的线程不是通过threading.Thread创建的,则返回一个功能受限的虚拟线程对象。
threading.get_ident()返回当前线程的线程标识符。注意当一个线程退出时,它的线程标识符可能会被之后新创建的线程复用。
threading.enumerate()返回当前存活的threading.Thread线程对象列表。
threading.main_thread()返回主线程对象,通常情况下,就是程序启动时Python解释器创建的threading._MainThread线程对象。
threading.stack_size([size])返回创建线程时使用的堆栈大小。也可以使用可选参数size指定之后创建线程时的堆栈大小,size可以是0或者一个不小于32KiB的正整数。如果参数没有指定,则默认为0。如果系统或者其他原因不支持改变堆栈大小,则会报RuntimeError错误;如果指定的堆栈大小不合法,则会报ValueError,但并不会修改这个堆栈的大小。32KiB是保证能解释器运行的最小堆栈大小,当然这个值会因为系统或者其他原因有限制,比如它要求的值是大于32KiB的某个值,只需根据要求修改即可。

三、threading常量

threading.TIMEOUT_MAX:指定阻塞函数(如Lock.acquire()、RLock.acquire()、Condition.wait()等)中参数timeout的最大值,在给这些阻塞函数传参时如果超过了这个指定的最大值会抛出OverflowError错误

四、线程对象:threading.Thread

threading.Thread目前还没有优先级和线程组的功能,而且创建的线程也不能被销毁、停止、暂定、恢复或中断。
守护线程:只有所有守护线程都结束,整个Python程序才会退出,但并不是说Python程序会等待守护线程运行完毕,相反,当程序退出时,如果还有守护线程在运行,程序会去强制终结所有守护线程,当守所有护线程都终结后,程序才会真正退出。可以通过修改daemon属性或者初始化线程时指定daemon参数来指定某个线程为守护线程。
非守护线程:一般创建的线程默认就是非守护线程,包括主线程也是,即在Python程序退出时,如果还有非守护线程在运行,程序会等待直到所有非守护线程都结束后才会退出。

:守护线程会在程序关闭时突然关闭(如果守护线程在程序关闭时还在运行),它们占用的资源可能没有被正确释放,比如正在修改文档内容等,需要谨慎使用

*threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, , daemon=None)
如果这个类的初始化方法被重写,请确保在重写的初始化方法中做任何事之前先调用threading.Thread类的__init__方法。

  1. group:应该设为None,即不用设置,使用默认值就好,因为这个参数是为了以后实现ThreadGroup类而保留的。
  2. target:在run方法中调用的可调用对象,即需要开启线程的可调用对象,比如函数或方法。
  3. name:线程名称,默认为“Thread-N”形式的名称,N为较小的十进制数。
  4. args:在参数target中传入的可调用对象的参数元组,默认为空元组()。
  5. kwargs:在参数target中传入的可调用对象的关键字参数字典,默认为空字典{}。
  6. 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。

使用threading.Thread类创建线程简单示例:

"""
通过实列化threading。Thread类创建线程
"""
import time
import threading 

def test_thread(para,sleep):
	"""线程运行函数"""
	time.sleep(sleep)
	print(para)
t
def main():
	# 创建线程
	thread_hi = threading.Thread(target = test_thread,args = ('hi',3))
	thread_hello = threading.Thread(target = test_thread,args = ('hello',1))
	# 启动线程
	thread_hi.start()
	thread_hello.start()
	# 返回当前存活的threading.Thread线程对象列表
	print(threading.enumerate())

if __name__ == '__main__':
    main()

在这里插入图片描述
使用threading.Thread类的子类创建线程简单示例:

# encoding = utf-8
"""
通过继承threading.Thread的子类创建线程
"""
import time
import threading
class TestThread(threading.Thread):
	def __init__(self,para,sleep):
		# 重写threading.Thread的__init__方法时,确保在所有操作之前先调用threading.Thread.__init__方法
		super().__init__()
		self.para = para
		self.sleep = sleep

	def run(self):
		"""线程内容"""
		time.sleep(self.sleep)
		print(self.para)

def main():
	# 创建线程
	thread_hi = TestThread('hi',3)
	thread_hello = TestThread('hello',1)
	# 启动线程
	thread_hi.start()
	thread_hello.start()
	print("Main thread has ended")


if __name__ == '__main__':
	main()

在这里插入图片描述
join方法

# encoding = utf-8
"""
通过继承threading.Thread的子类创建线程
"""
import time
import threading
class TestThread(threading.Thread):
	def __init__(self,para,sleep):
		# 重写threading.Thread的__init__方法时,确保在所有操作之前先调用threading.Thread.__init__方法
		super().__init__()
		self.para = para
		self.sleep = sleep

	def run(self):
		"""线程内容"""
		time.sleep(self.sleep)
		print(self.para)

def main():
	# 创建线程
	thread_hi = TestThread('hi',3)
	thread_hello = TestThread('hello',1)
	# 启动线程
	thread_hi.start()
	# 执行join方法会阻塞调用线程(主线程),直到调用join方法的线程(thread_hi)结束
	thread_hi.join()
	thread_hello.start()
	thread_hello.join()
	print("Main thread has ended")

if __name__ == '__main__':
	main()

使用join函数,会阻塞调用线程。直到调用join方法的线程(thread_xx)完全结束
在这里插入图片描述

五、锁对象:threading.Lock

threading.Lock 是直接通过_thread模块扩展实现的 当锁在被锁定时,它不属于某一个特定的线程.
锁只有“锁定”和“非锁定”两种状态,当锁被创建时,是处于“非锁定”状态的。当锁已经被锁定时,再次调用acquire()方法会被阻塞执行,直到锁被调用release()方法释放掉锁并将其状态改为“非锁定”。
同一个线程获取锁后,如果在释放锁之前再次获取锁会导致当前线程阻塞,除非有另外的线程来释放锁,如果只有一个线程,并且发生了这种情况,会导致这个线程一直阻塞下去,即形成了死锁。所以在获取锁时需要保证锁已经被释放掉了,或者使用递归锁来解决这种情况。

  1. acquire(blocking=True,
    timeout=-1):获取锁,并将锁的状态改为“锁定”,成功返回True,失败返回False。当一个线程获得锁时,会阻塞其他尝试获取锁的线程,直到这个锁被释放掉。timeout默认值为-1,即将无限阻塞等待直到获得锁,如果设为其他的值时(单位为秒的浮点数),将最多阻塞等待timeout指定的秒数。当blocking为False时,timeout参数被忽略,即没有获得锁也不进行阻塞。
  2. release():释放一个锁,并将其状态改为“非锁定”,需要注意的是任何线程都可以释放锁,不只是获得锁的线程(因为锁不属于特定的线程)。release()方法只能在锁处于“锁定”状态时调用,如果在“非锁定”状态时调用则会报RuntimeError错误。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值