17.9. _thread — 底层多线程 API


该模块提供了操作多个线程(也被称为 轻量级进程任务)的底层原语 —— 多个控制线程共享全局数据空间。为了处理同步问题,也提供了简单的锁机制(也称为 互斥锁二进制信号)。threading 模块基于该模块提供了更易用的高级多线程 API。

The module is optional. It is supported on Windows, Linux, SGI IRIX, Solaris 2.x, as well as on systems that have a POSIX thread (a.k.a. 「pthread」) implementation. For systems lacking the _thread module, the _dummy_thread module is available. It duplicates this module’s interface and can be used as a drop-in replacement.

It defines the following constants and functions:

exception _thread.error

发生线程相关错误时抛出。

3.3 版更變: 现在是内建异常 RuntimeError 的别名。

_thread.LockType

锁对象的类型。

_thread.start_new_thread(function, args[, kwargs])

启动一个线程,并返回其标识符。线程会用 args 作为参数(必须是元组)执行 function 函数。可选的 kwargs 参数使用字典来指定有名参数。当函数返回时,线程会静默退出,当函数由于未处理的异常而中止时,会打印一条堆栈追踪信息,然后该线程会退出(但其他线程还是会继续运行)。

_thread.interrupt_main()

Raise a KeyboardInterrupt exception in the main thread. A subthread can use this function to interrupt the main thread.

_thread.exit()

抛出 SystemExit 异常。如果没有捕获的话,这个异常会使线程退出。

_thread.allocate_lock()

返回一个新的锁对象。锁中的方法在后面描述。初始情况下锁处于解锁状态。

_thread.get_ident()

返回当前线程的 “线程标识符”。它是一个非零的整数。它的值没有直接含义,主要是用作 magic cookie,比如作为含有线程相关数据的字典的索引。线程标识符可能会在线程退出,新线程创建时被复用。

_thread.stack_size([size])

Return the thread stack size used when creating new threads. The optional size argument specifies the stack size to be used for subsequently created threads, and must be 0 (use platform or configured default) or a positive integer value of at least 32,768 (32 KiB). If size is not specified, 0 is used. If changing the thread stack size is unsupported, a RuntimeError is raised. If the specified stack size is invalid, a ValueError is raised and the stack size is unmodified. 32 KiB is currently the minimum supported stack size value to guarantee sufficient stack space for the interpreter itself. Note that some platforms may have particular restrictions on values for the stack size, such as requiring a minimum stack size > 32 KiB or requiring allocation in multiples of the system memory page size - platform documentation should be referred to for more information (4 KiB pages are common; using multiples of 4096 for the stack size is the suggested approach in the absence of more specific information). Availability: Windows, systems with POSIX threads.

_thread.TIMEOUT_MAX

Lock.acquire() 方法中 timeout 参数允许的最大值。传入超过这个值的 timeout 会抛出 OverflowError 异常。

3.2 版新加入.

锁对象有以下方法:

lock.acquire(waitflag=1, timeout=-1)

没有任何可选参数时,该方法无条件申请获得锁,有必要的话会等待其他线程释放锁(同时只有一个线程能获得锁 —— 这正是锁存在的原因)。

如果传入了整型参数 waitflag,具体的行为取决于传入的值:如果是 0 的话,只会在能够立刻获取到锁时才获取,不会等待,如果是非零的话,会像之前提到的一样,无条件获取锁。

如果传入正浮点数参数 timeout,相当于指定了返回之前等待得最大秒数。如果传入负的 timeout,相当于无限期等待。如果 waitflag 是 0 的话,不能指定 timeout

如果成功获取到所会返回 True,否则返回 False

3.2 版更變: 新的 timeout 形参。

3.2 版更變: 现在获取锁的操作可以被 POSIX 信号中断。

lock.release()

释放锁。锁必须已经被获取过,但不一定是同一个线程获取的。

lock.locked()

返回锁的状态:如果已被某个线程获取,返回 True,否则返回 False

除了这些方法之外,锁对象也可以通过 with 语句使用,例如:

import _thread

a_lock = _thread.allocate_lock()

with a_lock:
    print("a_lock is locked while this executes")

注意事项:

  • 线程与中断奇怪地交互:KeyboardInterrupt 异常可能会被任意一个线程捕获。(如果 signal 模块可用的话,中断总是会进入主线程。)

  • 调用 sys.exit() 或是抛出 SystemExit 异常等效于调用 _thread.exit()

  • 不可能中断锁的 acquire() 方法 —— KeyboardInterrupt 一场会在锁获取到之后发生。

  • 当主线程退出时,由系统决定其他线程是否存活。在大多数系统中,这些线程会直接被杀掉,不会执行 tryfinally 语句,也不会执行对象析构函数。

  • 当主线程退出时,不会进行正常的清理工作(除非使用了 tryfinally 语句),标准 I/O 文件也不会刷新。