from threading import xxx, xxx
threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)
参数如下:
.start()
启动新线程,开始线程活动。
它在一个线程里最多只能被调用一次,如果同一个线程对象中调用这个方法的次数大于一次,会抛出 RuntimeError 。
.run()
并不启动一个新线程,就是在主线程中调用函数或方法而已。
.join(timeout=None)
阻塞,直到此线程结束。
一个线程可以被 join() 很多次。
当 timeout 参数不存在或者是 None ,这个操作会阻塞直到线程结束。
name
返回只用于识别线程的名称,多个线程可以赋予相同的名称,初始名称一般为 Thread-x 格式。
is_alive()
返回线程是否存活,存活返回True,否则返回False。
daemon
一个表示这个线程是(True)否(False)守护线程的布尔值。一定要在调用 start() 前设置好,不然会抛出 RuntimeError 。初始值继承于创建线程,主线程不是守护线程,因此主线程创建的所有线程默认都是 daemon = False。
ident
这个线程的 ‘线程标识符’,如果线程尚未开始则为 None,这是个非零整数。
native_id
此线程的原生集成线程 ID。 这是一个非负整数,或者如果线程还未启动则为 None。
注解:类似于进程 ID,线程 ID 的有效期(全系统范围内保证唯一)将从线程被创建开始直到线程被终结。
示例
import time
from threading import Thread
def funx():
print("这是一个函数")
time.sleep(3)
p = Thread(target=funx)
p.daemon = True # 开启守护进程,默认为False
p.start() # 启用新线程
print(p.name) # 查看线程名称
print(p.ident) # 查看线程标识符
print(p.native_id) # 查看原生集成线程 ID
p.join(timeout=1) # 等待线程结束,超时时间为1秒
print(p.is_alive()) # 判断线程是否存活,此时返回为True(存活)
threading.Timer(interval, function, args=None, kwargs=None)
创建一个定时器,在经过 interval 秒的间隔事件后,将会用参数 args 和关键字参数 kwargs 调用 function。
参数如下:
示例
使用定时器做一个简易的自动刷新验证码功能
class Code:
def __init__(self, sec):
if not isinstance(sec, float):
sec = float(sec)
self.sec = sec
self.code = self.mark_code()
@staticmethod
def mark_code():
return "".join(random.sample(string.ascii_letters+string.digits, 4))
def refresh_code(self):
self.code = self.mark_code()
print(self.code) # 打印验证码
self.t_code = Timer(self.sec, self.refresh_code) # 调用函数自己
self.t_code.start()
def run(self):
self.refresh_code()
while True:
enter = input("请输入验证码:")
if enter.lower() == self.code.lower():
print("验证成功")
self.t_code.cancel()
break
code = Code(5)
code.run()
实现原始锁对象的类,一旦一个线程获得一个锁,会阻塞其他尝试获得这个锁的其他线程,直到它被释放。
.acquire(blocking=True, timeout=-1)
可以阻塞或非阻塞地获得锁,如果成功获得锁,则返回 True,否则返回 False (例如发生 超时 的时候)。
blocking 设置为 True (缺省值),阻塞直到锁被释放,然后将锁锁定并返回 True 。在参数 blocking 被设置为 False 的情况下调用,将不会发生阻塞。如果调用时 blocking 设为 True 会阻塞,并立即返回 False ,否则,将锁锁定并返回 True。
.timeout 为浮点数类型,参数被设置为正值调用时,只要无法获得锁,将最多阻塞 timeout 设定的秒数,timeout 参数被设置为 -1 时将无限等待。当 blocking 为 false 时,timeout 指定的值将被忽略。
.release()
释放一个锁,没有返回值。
当锁被锁定,将它重置为未锁定(简单的来说就是释放这把锁),并返回,如果其他线程正在等待这个锁解锁而被阻塞,释放后线程们开始抢锁。
在未锁定的锁调用时,会引发 RuntimeError 异常。
locked()
判断是否获得锁,如果获得了锁则返回True,否则返回False。
示例
死锁现象展示
mutexA = Lock() # 锁A
mutexB = Lock() # 锁A
class MyThread(Thread):
def __init__(self):
super().__init__()
def funx1(self):
mutexA.acquire()
print(f"{self.name} 拿到A锁")
mutexB.acquire()
print(f"{self.name} 拿到B锁")
mutexB.release()
mutexA.release()
def funx2(self):
mutexB.acquire()
print(f"{self.name} 二次拿到B锁")
time.sleep(0.1) # 线程速度很快,这里要加个休眠保证一直拿着B锁
mutexA.acquire()
print(f"{self.name} 二次拿到A锁")
mutexA.release()
mutexB.release()
def run(self) -> None:
self.funx1()
self.funx2()
for i in range(3):
t = MyThread()
t.start()
一个线程拿着A锁想再去获得B锁,而另一个线程拿着B锁想再去获得A锁,两个线程互相打结,就像两个线程被困在各自的房间,而门钥匙又互相在对方手里,这种现象称为死锁,参考打结时打的死结。
此类实现了重入锁,重入锁必须由获取它的线程释放,一旦线程获得了重入锁,同一个线程再次获取它将不阻塞,线程必须在每次获取它时释放一次。简单来说:每锁一次相当于做了一次加1操作(初始值0),锁了两次计数为2,解锁一次相当于减1操作,只有一直减到0(初始值),才能真正解锁给其他线程使用。
acquire(blocking=True, timeout=-1)
可以阻塞或非阻塞地获得锁。
当无参数调用时: 如果这个线程已经拥有锁,递归级别增加一,并立即返回。否则,如果其他线程拥有该锁,则阻塞至该锁解锁。一旦锁被解锁(不属于任何线程),则抢夺所有权,设置递归等级为一,并返回,如果多个线程被阻塞,等待锁被解锁,一次只有一个线程能抢到锁的所有权,在这种情况下,没有返回值。
blocking 参数设为布尔值,默认True则执行与无参数调用时一样的操作,然后返回 True。设为False,则不进行阻塞。
timeout 为浮点数类型,timeout 参数设为正值时,只要无法获得锁,将最多阻塞 timeout 所指定的秒数时长,如果已经获得锁则返回 True,如果超时则返回False。
release()
释放锁,自减递归等级,如果减到零,则将锁重置为非锁定状态(不被任何线程拥有),并且,如果其他线程正被阻塞着等待锁被解锁,则其他线程开始抢这把锁,如果自减后,递归等级仍然不是零,则锁保持锁定,仍由目前所调用的线程拥有,没有返回值。
在未锁定的锁调用时,会引发 RuntimeError 异常。
示例
通过递归锁解决死锁问题
mutexA = mutexB = RLock() # 将锁A与锁B合并为同一把锁
class MyThread(Thread):
def __init__(self):
super().__init__()
def funx1(self):
mutexA.acquire()
print(f"{self.name} 拿到A锁")
mutexB.acquire()
print(f"{self.name} 拿到B锁")
mutexB.release()
mutexA.release()
def funx2(self):
mutexB.acquire()
print(f"{self.name} 二次拿到B锁")
time.sleep(0.1) # 线程速度很快,这里要加个休眠保证一直拿着B锁
mutexA.acquire()
print(f"{self.name} 二次拿到A锁")
mutexA.release()
mutexB.release()
def run(self) -> None:
self.funx1()
self.funx2()
for i in range(3):
t = MyThread()
t.start()
返回当前对应调用者的控制线程的 Thread 对象。如果调用者的控制线程不是利用 threading 创建,会返回一个功能受限的虚拟线程对象。