一文读懂多线程

Posted python爬虫小助手

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一文读懂多线程相关的知识,希望对你有一定的参考价值。


1threading简介


我们首先看看官方文档里面关于threading这个模块的介绍。

threading模块是建立在_thread模块的高级模块,同样是一个内置模块,它具有更为高级的多线程接口。

threading中的模块接口:

threading.active_count():返回当前存货的进程数量。

threading.current_thread():返回当前线程对象,对应于调用者控制的线程。

threading.get_ident():返回当前线程的标识符,是一个非零整数,线程标识符可以在线程退出时循环使用,并且创建另一个线程。

threading.enumerate():返回当前存在的所有线程对象的列表,这个列表包含了daemonic线程和由current_thread()创建的虚拟线程对象,以及主线程。

threading.main_thread():返回主线程对象。一般情况下,主线程由python解释器开启。

threading.settrace(func):为所有线程设置追踪函数,在run方法开启前,该函数将传递给sys.settrace()。

threading.TIMEOUT_MAX:阻塞函数的超时参数允许的最大值,指定大于此值的超时将引发溢出错误。

class threading.local:代表着 thread-local 数据的类。


2threading.Thread对象


Thread类表示一个在单独的控制线程中运行的活动。

通过重写这个类的__init__()run()方法,来指明这个线程的活性。

一旦线程被创建,调用线程的start()方法后,这个线被启动,此刻这个线程是“alive”的。线程的run()方法终止时(通常是正常的终止的),或者通过抛出未处理的异常,它将停止存活。

is_alive()方法可以用来测试线程是否存在。

所有的线程都可以调用join()方法,它将持续阻塞,直到所有执行了该方法的线程执行完毕任务之后。

调用name()方法可以获得线程的name属性。

线程都可以用setDaemon()方法标记守护线程,必须在线程启动之前设置。这个标志的意义是守护线程被守护到直至整个python程序退出前。如果希望线程优雅地停止,一定不要让它成为守护线程,可以尝试用Event事件等信号机制来实现。主线程,对应于Python程序中的初始控制线程,并不是守护线程。

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

group是未来可能补充进来的 ThreadGroup 的扩展;

targetrun方法调用的可调用对象。

name是线程名字。

args是传入target的参数。

如果子类重写该构造函数,它必须确保在执行线程之前调用基类构造函数(thread.__init__())

Cpython中,基于全局解释器锁(GlobalInterpreter Lock),同一时间,只能有一个线程执行python代码(某些以性能为导向的库可能会克服这种限制),如果你希望你的应用程序充分利用电脑多核资源,可以尝试使用:

多进程或者 concurrent.futures.ProcessPoolExecutor


Lock Objects,原始锁是一种同步原语,它在锁定时不是由特定线程拥有的。一个原始锁有两种状态,锁住或者解锁状态。线程被激活后,一般处于解锁状态。这两种状态对应着两个基本函数: acquire() release()acquire可以改变解锁状态为阻塞状态,当线程被锁住后,等待release回调,锁被释放,重新变为解锁状态。支持上下文管理协议。

上下文管理协议,即这个模块提供的所有对象实现acquire()release()方法,可以作为with语句实现。

with some_lock:

    # do something...

完全等价于:

some_lock.acquire()

try:

    # do something...

finally:

    some_lock.release()


RLock Objects,可重入锁是一个同步原语,它可以被同一个线程多次获取。与lockobject类似,拥有两个基本函数方法,只是在锁住状态时可获取多重锁,在解锁状态时被释放。支持上下文管理协议。


3线程通信


 Condition Objects,条件变量也遵循上下文管理协议,来管理acquire()release()方法;其他方法,如wait()方法释放锁,然后阻塞至另一个线程通过notify()或者notify_all()方法回调唤醒,一旦被唤醒,wait方法重新获得锁并返回。注意,notifynotify_all方法并不能释放锁,这意味着线程唤醒并不会立即从wait返回,直到被notify才会真正释放锁的占有权。

例如,生产者和消费者模型:

# Consume one item

with cv:

    while not an_item_is_available():

        cv.wait()

    get_an_available_item()

# Produce one item

with cv:

    make_an_item_available()

    cv.notify()

 

Semaphore Objects,一个信号量管理着一个内部计数器,这个计数被每被acquire()调用一次递减,又通过每个release()调用递增。如果acquire发现这个计数是0,就会阻塞,直到其他线程调用release()。支持上下文管理协议。

class threading.Semaphore(value=1)

class threading.BoundedSemaphore(value=1)

maxconnections = 5

pool_sema =BoundedSemaphore(value=maxconnections)

with pool_sema:

    conn= connectdb()

    try:

       # ... use connection ...

    finally:

       conn.close()

 

Event Objects,这是线程之间通信最简单的交流方式之一。一个线程设置事件,其他线程等待它。事件对象管理可以用set()方法设置为true,并使用clear()方法重置为falsewait()方法阻塞直到flagtrue

is_set():当且仅当内部标志为真时返回true

set():设置internal flagTrue,所有的线程等待它为真才能唤醒。

Clear():重新设置内部flagFasle。调用wait()的线程将阻塞直到set()被调用,以便再次将内部flag设置为True

wait(timeout=None):阻塞直至内部flagTrue。如果内部flagTrue,立即返回,否则,将会阻塞至另一个线程调用set()

 

Timer Objects,这个类表示经过一定时间才能运行的操作。

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

def hello():

   print("hello, world")

t = Timer(30.0, hello)

t.start()

 # after 30 seconds, "hello, world" will be printed


4例子


关于多线程的实例,网上有很多,这里给大家安利一个我看过的一篇公众推文,作者是大牛:


好了,线程的分享就到这里了。

本文翻译至threading官方文档,点击下方“阅读原文”进行详细了解。


o长按关注o







 

 

 

 

 

 

 

 

 

 

 


以上是关于一文读懂多线程的主要内容,如果未能解决你的问题,请参考以下文章

读懂多线程,全靠这500多页Java并发多线程源码笔记

一文让你彻底搞懂多线程

python3高级编程一文搞懂多进程

一文读懂JAVA多线程

一文读懂JAVA多线程

一文让你读懂高并发编程的意义及其好处和注意事项