当前位置:   article > 正文

Python并发编程之threading模块_python threading 参数

python threading 参数

threading 模块

threading模块提供Thread类和各种同步原语,用于编写多线程的程序。

Thread类用于表示单独的控制线程。使用下面的函数可以创建一个新的线程。

Thread(group=None, target=None, name=None, args=(), kwargs={})
  • 1

此函数创建一个新的Thread实例。

  • group的值是none,为以后的版本而保留。
  • target是一个可调用对象,线程启动时,run()方法将调用此对象,它的默认值是none,表示不调用任何内容。
  • name是线程名称。默认键创建一个"Tread-N"格式的唯一名称。
  • args是传递给target函数的参数元组。
  • kwargs是传递个target的关键字参数的字典

Thread的实例t支持以下方法和属性。

  • t.start()
  • t.run()
  • t.join([timeout])
  • t.is_alive()
  • t.name()
  • t.ident
  • t.daemon

下面这个例子说明如何以线程的形式创建和启动一个函数。

import threading
import time

def clock(interval):
    while True:
        print("the time is %s" % time.ctime())
        time.sleep(interval)
        
t = threading.Thread(target=clock,args=(5, ))
t.daemon = True
t.start()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

下面这个例子说明如何将同一个线程定义为一个类:

class ClockThread(threading.Thread):
    def __init__(self, interval):
        threading.Thread.__init__(self)
        self.daemon = True
        self.interval = interval
        
    def run(self):
        while True:
            print("the time is %s" % time.ctime())
            time.sleep(self.interval)
            
t = ClockThread(5)
t.start()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

如果将线程定义为类,并且定义自己的__init__()方法,必须像上面这样调用基类构造函数Thread.init(), 如果忘记这一点,将导致严重错误。除了run()方法外,改写线程已经定义好的其他方法也会出现错误。

以上例子中,对于daemon属性的设置,是永远在后台运行的线程的常见功能。通常python在解释器退出之前,会等待所有线程终止。设置daemon标志会使解释器在主程序退出后立即退出。在这种情况下,daemonic线程将被销毁。


1. Timer对象

Timer对象用于在稍后的某个时间执行一个函数。

Timer(interval, func [, args [, kwargs]])
  • 1

创建定时器对象,在过去interval秒时间之后运行函数func。 args和kwargs是传递给func的参数和关键字参数。在调用start()方法后悔启动定时器。

Timer对象实例t具有以下方法:

  • t.start()
  • t.cancel()

2. Lock对象

原语锁定(或互斥锁定)是一个同步原语,状态是“已锁定”或“未锁定”之一。两个方法acquire()和release()用于修改锁定的状态。如果状态为锁定,尝试获取锁定就被阻塞,直到锁定被释放为止。如果有多个线程等待获取锁定,当锁定被释放时,只有一个线程能够得到它。等待线程获得锁定的顺序是未定义的。

使用下面的构造函数创建新的Lock对象, 初始状态为“未锁定”

Lock()
  • 1

Lock对象的实例lock支持以下方法:

  • lock.acquire([blocking])
  • lock.release()

3. RLock

可重入锁定是一个类似于Lock对象的同步原语,但同一个线程可以多次获取它。这允许拥有锁定的线程执行嵌套的acquire()和release()操作。在这种情况下,只有最外面的release()操作才能将锁定重置为未锁定状态.

使用下面的构造函数可以创建一个新的RLock对象

RLock()
  • 1

RLock的实例rlock支持以下方法:

  • rlock.acquire([blocking])
  • rlock.release()

4. 信号量和有边界的信号量

信号量是一个机遇计数器的同步原语,每次调用acquire()方法时此计数器的值减1, 每次调用release()时此计数器的值加1.如果计数器为0,acquire()方法将会阻塞,知道其他线程调用release()方法为止。

Semaphore([value])
  • 1

创建一个新的信号量。value是计数器的初始值。如果省略此参数,计数器将被置为1.

Semaphore的实例s支持以下方法:

  • s.acquire([blocking])
  • s.release()
BoundedSemaphore([value])
  • 1

创建一个新的信号机。value是计数器的初始值。如果省略此参数,计数器的值将被置为1.
BoundedSemaphore的工作方式和Semaphore完全相同,但release()操作的次数不能超过acquire()操作的次数。

信号机和互斥锁之间的微妙差别在于:信号机可用于发射信号。例如,可以从不同线程调用acquire()和release()方法,以便在生产者和消费者线程之间进行通信。


5. 事件

事件用于在线程之间通信。一个线程发出“事件”信号,一个或多个其他线程等待它。Event实例管理着一个内部标志,可以使用set()方法将它置为True, 或者使用clear()方法将它重置为false。wait()方法将阻塞直到标志位true。

Event()
  • 1

创建新的Event实例,并将内部标志置为false。Event实例e具有以下方法:

  • e.is_set()
  • e.set()
  • e.clear()
  • e.wait([timeout])

尽管Event对象可用于给其他线程发信号,但不应该使用它们来实现在生产者/消费者问题中十分典型的通知。例如,应该避免写出下面这样的代码:

evt = Event()

def producer():
    while True:
        # 生产项目
        ...
        evt.signal()

def consumer():
    while True:
        # 等待一个项目
        evt.wait()
        # 使用项目
        ...
        # 清除事件并再次等待
        evt.clear()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

这段代码并不可靠,因为在evt.wait()和evt.clear()之间,生产者可能生产了一个新的项目。但是通过清除事件,在生产者创建一个新项目之前,使用者可能看不到这个新的项目。最好的情况是,程序将经过一段很短的停滞,对项目的处理被莫名其妙推迟了。最坏的情况是,由于事件信号丢失,整个程序将会挂起。要解决这类问题,最好使用条件变量。


6. 条件变量

条件变量是构建在另一个锁定上的同步原语。但需要线程关注特定的状态变化或事件的发生时将使用这个锁定。典型的用法是生产者/消费者问题,其中一个线程生产的数据供另一个线程使用。

使用下面的构造函数可以创建一个新的Condition实例:

Condition([lock])
  • 1

创建新的条件变量。lock是可选的Lock或者RLock实例。如果未提供Lock参数,就会创建新的RLock实例供条件变量使用。

条件变量cv支持以下方法:

  • cv.acquire(*args)
  • cv.release()
  • cv.wait([timeout])
  • cv.notify([n])
  • cv.notify_all()

下面这个例子提供了使用条件变量的模板:

cv = threading.Condition()
def producer():
    while True:
        cv.acquire()
        produce_item()
        cv.notify()
        cv.release()

def consumer():
    while True:
        cv.acquire()
        while not item_is_available():
            cv.wait()   # 等待项目出现
        cv.release()
        consume_item()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

使用条件变量时需要注意的是,如果存在多个线程等待同一个条件,notify()操作可能唤醒它们中的一个或多个。因此,始终有这样的可能:某个线程被唤醒后,却发现它等待的条件不存在了。这解释了在consumer()函数中使用while循环的原因。如果线程醒来,但是生产的项目已经消失,它就会回去等待下一个信号。


7. 使用Lock

使用诸如Lock、RLock或Semaphore之类的锁定原语时,必须多加小心。锁定的错误管理经常导致死锁或竞争条件。依赖锁定的代码应该保证当出现异常时正确地释放锁定。典型的代码如下:

try:
    lock.acquire()
    # 关键部分
    statements
    ...
finally:
    lock.release()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

另外,所有锁定还支持上下文管理协议(一个小小的清理器):

with lock:
    # 关键部分
    statements
    ...
  • 1
  • 2
  • 3
  • 4

在上面例子中,with语句自动获取锁定,并且在控制流离开上下文时自动释放锁定。

此外,编写代码时一般应该避免同时获取多个锁定,例如:

with lock_A:
    # 关键部分
    statements
    ...
    with lock_B:
        # B的关键部分
        statements
        ...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

这种写法通常很容易导致应用程序神秘死锁。尽管有几种策略可以避免出现这种情况(例如分层锁定),但最好在编写代码时就避免这种写法。


8. 线程终止与挂起

线程没有任何方法可用于强制终止或挂起。这是设计上的原因,因为编写线程程序本身就十分复杂。例如,如果某个线程已经获取了锁定,在它能够释放锁定之前强制终止或挂起它,将导致整个应用程序出现死锁。此外,终止时一般不能简单地“释放锁定”, 因为复杂的线程同步经常涉及锁定和解除锁定操作,而这些操作在执行时的次序是要十分精确的。

如果腰围终止或挂起提供支持,需要自己构建这些功能。一般的做法是在循环中运行线程,这个循环的作用是定期检查线程状态以决定它是否应该终止


9. 实用工具函数

  • active_count()
  • current_thread()
  • enumerate()
  • local()
  • setprofile(func)
  • settrace(func)
  • stack_size([size])

10. 全局解释器锁定 GIL(Global Interpreter Lock)

python解释器被一个锁定保护,该锁定只允许一次执行一个线程,即便存在多个可用的处理器。

在计算密集型程序中,这严重限制了线程的作用, 事实上,在计算密集型应用程序中使用线程,经常比仅仅按照顺序执行同样的工作慢的多。因此,实际上应该只在主要关注I/O的程序,如网络服务器中使用线程。对于计算密度更高的任务,最好使用C扩展模块或multiprocessing模块来代替。C扩展具有释放解释器锁定和并行运行的选项,可以做到当释放锁定时不与解释器进行交互。multiprocessing模块将工作分派给不受锁定限制的单独子进程。


11. 使用线程编程

尽管在python中可以使用各种锁定和同步原语的组合编写非常传统的多线程程序,但有一种首推的编程方式要优于其他所有编程方法,即将多线程程序组织为多个独立任务的集合,在这些任务之间通过消息队列进行通信。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/768859
推荐阅读
相关标签
  

闽ICP备14008679号