python多线程

本文章使用 python3.7,详情可查看官方内容:
https://docs.python.org/3.7/library/threading.html#module-threading

threading 使用的是高等级api,与之对应的是 _thread ,它是低等级的api,提供更复杂更底层的使用。

threading

threading. active_count ()

返回所有存活的线程对象数量,这个数量等于 enumerate() 获取到的 list 长度

threading. current_thread ()

返回目前的线程对象,对应于调用者的线程控制。如果调用者的线程控制没有通过 threading 创建,将会返回功能受限的虚拟线程对象。

threading. get_ident ()
返回当前线程的识别码。它是非零整数,有各种用途,比如索引线程特有的数据目录。当线程退出,创建新的线程时,线程识别码可能被再次使用。

threading. enumerate ()
返回所有存活线程的对象列表,包括守护线程,被 current_thread() 创建的虚拟线程对象和主线程。不包括已经结束的线程和没有启动的线程。

threading. main_thread ()

返回主线程对象,通常情况下,主线程是启动 python 解释器的那个线程。

threading. settrace ( func )

为启动的线程设置追踪函数。对于每一个线程,在调用 run 方法之前,会把 func 参数传递给 sys.settrace()

threading. setprofile ( func )

为所有线程设置一个轮廓函数。对于每一个线程,在调用 run 方法之前,会把 func 参数传递给 sys.setprofile()

threading. stack_size ([ size ])

返回线程的栈容量。可选参数 size 指定随后创建的线程栈大小,只能是 0 或者正数值(至少32,768,即32KiB)。如果没指定 size ,默认使用 0 。如果不能修改线程的栈容量,会抛出 RuntimeError 异常。如果指定非法的参数,会抛出 ValueError 异常并且栈的大小不会被更改。栈的最小值为32 KiB,用来保证解释器有足够的栈空间。 请注意,某些平台可能会对栈容量有特定限制。

该功能可在 windows ,支持 POSIX 线程的系统上使用。

threading. TIMEOUT_MAX

函数被阻塞的最大时间( Lock.acquire() , RLock.acquire() , Condition.wait() , etc.),如果指定的超时值大于此值,将引发 OverflowError 异常。

threading 模块借鉴了 java 线程模块, java 对每个对象都使用锁和条件变量,但 python 中是独立的对象。Python 的线程类支持 java 线程类的所有动作。目前没有优先级,没有线程组,并且无法销毁,停止,挂起,恢复或中断线程。 Java Thread 类的静态方法在实现后会映射到模块级函数。

线程局部数据

线程局部数据是每个线程独有的数据,可以创建一个 local (or a subclass) 实例,然后这个实例中存数据,比如下面代码:

mydata = threading.local()
mydata.x = 1

不同线程的实例值可以不同,更多细节可参考 _threading_local 模块。

线程对象

Thread 类表示在单独的控制线程中运行的活动。有两种活动定义方式:向构造器传递一个可被调用的对象或者在子类中重写 run() 方法。只要创建好线程对象,就可以调用线程的 start() 方法启动活动。它会在每个独立的控制线程中调用 run() 方法。一旦线程启动了活动,那么它就处于存活状态,当 run() 方法终止运行(可能正常退出,也可能抛出异常)时,就会退出存活状态,可以用 is_alive() 方法查看线程是否处于存活状态。

线程可以调用 join() 方法,它将阻塞此线程,直到其他调用 join() 方法的线程结束运行。

多线程代码及实现

不使用任何多线程时,代码会执行 6s 后结束;

"""
This module provides multithreading example
"""

from time import sleep, ctime


def loop0():
    """sleep 4s and print time"""
    print('start loop0 at ' + ctime())
    sleep(4)
    print('end loop0 at ' + ctime())


def loop1():
    """sleep 2s and print run time"""
    print('start loop1 at ' + ctime())
    sleep(2)
    print('end loop1 at ' + ctime())


def main():
    """invoking loop0 and loop1 , print time"""
    print("all start at" + ctime())
    loop0()
    loop1()
    print("all end at " + ctime())


if __name__ == '__main__':
    main()

使用 _thread 方式,利用 _thread.start_new_thread() 函数创建子线程,并使用 sleep(6) 保持主线程不退出:

"""
This module provides multithreading example
"""

from time import sleep, ctime
import _thread


def loop0():
    """sleep 4s and print time"""
    print('start loop0 at ' + ctime())
    sleep(4)
    print('end loop0 at ' + ctime())


def loop1():
    """sleep 2s and print time"""
    print('start loop1 at ' + ctime())
    sleep(2)
    print('end loop1 at ' + ctime())


def main():
    """using _thread invoke loop0 and loop1 , print time"""
    print("all start at" + ctime())
    _thread.start_new_thread(loop0, ())
    _thread.start_new_thread(loop1, ())
    sleep(6)
    print("all end at " + ctime())


if __name__ == '__main__':
    main()

使用改进的 _thread ,利用 _thread 锁实现主线程自动监视子线程是否解锁:

"""
This module provides multithreading example
"""

from time import sleep, ctime
import _thread

LOOPS = [4, 2]


def loop(nloop, nsec, lock):
    """
    Sleep time according to parameters

    :param nloop: thread name
    :param nsec:  sleep time
    :param lock:  using lock
    :return: pass
    """
    print("start loop", nloop, "at" + ctime())
    sleep(nsec)
    print('loop', nloop, "at" + ctime())
    lock.release()


def main():
    """
    setting lock for every thread
    invok loop according to loops

    :return: pass
    """
    print("starting at" + ctime())
    locks = []
    nloops = range(len(LOOPS))
    for i in nloops:
        lock = _thread.allocate_lock()
        lock.acquire()
        locks.append(lock)
    for i in nloops:
        _thread.start_new_thread(loop, (i, LOOPS[i], locks[i]))
    for i in nloops:
        while locks[i].locked():
            pass
    print("all stop")


if __name__ == '__main__':
    main()

其它方法(todo 代码 PEP8 格式优化, todo 解释)。

threading

import logging
import threading
from time import sleep, ctime

logging.basicConfig(level=logging.INFO)

loops = [2, 4, 6]

class MyThread(threading, Thread):
def init(self, func, args, name=’’):
threading.Thread.init(self)
self.func = func
self.args = args
self.name = name

def run(self):
    self.func(*self.args)

def loop(nloop, nsec):
logging.info(“Start loop” + str(nloop)+" at " + ctime())
sleep(nsec)
logging.info(“End loop " + str(nloop)+” at " + ctime())

def main():
logging.info("START ALL AT " + ctime())
threads = []
nloops = range(len(loops))
for i in nloops:
t = MyThread(loop, (i, loops[i]), loop.name)
threads.append(t)
for i in nloops:
threads[i].start()
for i in nloops:
threads[i].join()
logging.info("END ALL AT " + ctime())

if name == ‘main’:
main()