Python 标准库之threading 开启线程

Python 标准库之threading 开启线程_第1张图片

Python 标准库之threading 开启线程

文章目录

  • Python 标准库之threading 开启线程
    • 使用方式
    • threading.Thread
      • 类功能:
    • threading.Timer
      • 类功能:
    • threading.Lock
      • 类功能:
    • threading.RLock(递归锁)
    • .current_thread()


使用方式

from threading import xxx, xxx

threading.Thread

threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)

参数如下:

  • group: 应该为 None;为了日后扩展 ThreadGroup 类实现而保留。
  • target: 是用于 run() 方法调用的可调用对象,比如函数或方法,默认是 None,表示不需要调用任何方法。
  • name: 是线程名称,默认情况下,由 “Thread-N” 格式构成一个唯一的名称,其中 N 是小的十进制数,从1开始。
  • args:在参数target中传入的可调用对象的参数元组,默认为空元组()。
  • kwargs: 是用于调用目标函数的关键字参数字典。默认是 {}。
  • 如果是 None (默认值),线程将继承**当前线程(一般就是主线程)**的守护进程模式属性,除了None值,无论输入什么参数,都会设置为守护进程模式

类功能:

  • .start()
    启动新线程,开始线程活动。
    它在一个线程里最多只能被调用一次,如果同一个线程对象中调用这个方法的次数大于一次,会抛出 RuntimeError 。

  • .run()
    并不启动一个新线程,就是在主线程中调用函数或方法而已。

  • .join(timeout=None)
    阻塞,直到此线程结束。

    • timeout为超时选项,以秒为单位的浮点数,如果线程运行时间超过所设定值时间,将不再等待此线程结束。在join之后使用 .is_alive() 可以判断线程是否还存活,若超时join不再等待将为True(存活)。

    一个线程可以被 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

threading.Timer(interval, function, args=None, kwargs=None)

创建一个定时器,在经过 interval 秒的间隔事件后,将会用参数 args 和关键字参数 kwargs 调用 function。
参数如下:

  • interval:间隔时间,单位为秒。(位置参数)
  • function:执行的函数或方法(位置参数)
  • args:传入function的参数,如果为None,则会传入一个空列表。
  • kwargs:传入function的关键字参数,如果为None,则会传入一个空字典。

类功能:

  • .cancel()
    停止定时器并取消执行计时器将要执行的操作,仅当计时器仍处于等待状态时有效。

示例
使用定时器做一个简易的自动刷新验证码功能

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()

threading.Lock

实现原始锁对象的类,一旦一个线程获得一个锁,会阻塞其他尝试获得这个锁的其他线程,直到它被释放。

类功能:

  • .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锁,两个线程互相打结,就像两个线程被困在各自的房间,而门钥匙又互相在对方手里,这种现象称为死锁,参考打结时打的死结。

threading.RLock(递归锁)

此类实现了重入锁,重入锁必须由获取它的线程释放,一旦线程获得了重入锁,同一个线程再次获取它将不阻塞,线程必须在每次获取它时释放一次。简单来说:每锁一次相当于做了一次加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()

.current_thread()

返回当前对应调用者的控制线程的 Thread 对象。如果调用者的控制线程不是利用 threading 创建,会返回一个功能受限的虚拟线程对象。

你可能感兴趣的:(python,多线程,thread)