• 多线程


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

    Thread对象

    • Thread类用于表示单独的控制线程。使用下面的函数可以创建一个新线程。
    • Thread(group=None, target=None, namesNone, args=(), kwargs=())
    • Group的值是None,为以后的扩展而保留。
    • target是一个可调用对象,线程启动时,run(方法将调用此对象,它的默认值是None,表示不调用任何内容。
    • name是线程名称。默认将创建一个" Thread-N格式的唯一名称。
    • args是传递给 target函数的参数元组。
    • kwargs是传递给 target的关键字参数的字典。

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

    1. t.start():通过在一个单独的控制线程中调用run()方法,启动线程。此方法只能调用一次。

    2. t.run():线程启动时将调用此方法。默认情况下,它将调用传递到构造函数中的目标函数。还可以在 Thread的子类中重新定义此方法。

    3. t.join([timeout]):等待直到线程终止或者出现超时为止。 timeout是一个浮点数,用于指定以秒为单位的超时时间。线程不能连接自身,而且在线程启动之前就连接它将出现错误。

    4. t.is_alive():如果线程是活动的,返回rue,否则返回 False。从 start()方法返回的那一刻开始,线程就是活动的,直到它的run()方法终止为止。

    5. t.name:线程名称

    6. t.ident:整数线程标识符。如果线程尚未启动,它的值为None。

    7. t.daemon:线程的布尔型后台标志。必须在调用 start()方法之前设置这个标志,它的初始值从创建线程的

    后台状态继承而来。当不存在任何活动的非后台线程时,整个 Python程序将退出。所有程序都有一个

    主线程,代表初始的控制线程,它不是后台线程。

    • 如何以线程的形式创建和启动一个函数(或其他可调用对象)

    直接调用线程:

    import threading
    import time
    def sayhi(num):
        print("启动第几个:%s"%num)
    
        time.sleep(3)
    if __name__ == '__main__':
        t1 = threading.Thread(target=sayhi,args=(1,))
        t2 = threading.Thread(target=sayhi,args=(2,))
    
        t1.start()
        t2.start()
        print(t1.getName())
        print(t2.getName())
    

    继承式调用:

    import threading
    import time
    class myThread():
        def __init__(self,num):
            threading.Thread.__init__(self)
            self.num = num
    
        def run(self):
            print("启动第几个:%s" %self.num)
            time.sleep(3)
    if __name__ == '__main__':
        t1 = myThread(1)
        t2 = myThread(2)
    
    
        t1.run()
        t2.run()
    

    Timer对象

    • Timer对象用于在稍后的某个时间执行一个函数。
    • Timer(interval, func [, args [, kwargs]])
    • 创建定时器对象,在过去 interval秒时间之后运行函数 func. args和 kwargs提供传递给func的参数和关键字参数。在调用 start()方法后才会启动定时器。
    • Timer对象t具有以下方法。
    1. t.start:启动定时器。提供给Timer()方法的函数func将在指定的时间间隔之后执行。
    2. t.cancel():如果函数尚未执行,取消定时器

    Lock对象

    • 互斥锁,两个方法 acquire()和 releaseω)用于修改锁的状态。如果状态为已锁定,尝试获取锁将被阻塞,直到锁被释放为止。如果有多个线程等待获取锁,当锁被释放时,只有一个线程能获得它。等待线程获得锁的顺序没有定义。
    • 使用下面的构造函数可以创建新的Lock实例:
      lock = thraeding.Lock()
    • 创建新的Lock对象,初始状态为未锁定。
    • Lock实例1ock支持以下方法。
    • lock.acquire([blocking 1):获取锁,如果有必要,需要阻塞到锁释放为止。如果提供 blocking参数并将它设为 False,当无法获取锁时将立即返回 False,如果成功获取锁则返回True。
    • lock.release()
    • 释放一个锁。当锁处于未锁定状态时,或者从与原本调用 acquire()方法的线程不同的线程调用此方法,将出现错误。

    Rlock对象

    • 可重入锁是一个类似于Lock对象的同步原语,但同一个线程可以多次获取它。这允许拥有锁的线程执行嵌套的 acquire()和 release()操作。在这种情况下,只有最外面的 release()操作才能将锁重置为未锁定状态。
      使用下面的构造函数可以创建一个新的 RLock对象:
    rlock = threading.RLock()
    
    • 创建新的可重入锁对象。 RLock对象 rlock支持以下方法。
    • rlock.acquire([blocking 1):获取锁,如果有必要,需要阻塞到锁被释放为止。如果没有线程拥有锁,它将被锁定,而且递归级别被置为1。如果此线程已经拥有锁,锁的递归级别加1,而且函数立即返回.
    • rlock.release()通过减少锁的递归级别来释放它。如果在减值后递归级别为0,锁将被重置为未锁定状态。否则,锁将保持已锁定状态。只能由目前拥有锁的线程来调用此函数。

    信号量

    • 信号量是一个基于计数器的同步原语,每次调用 acquire()方法时此计数器减1,每次调用release()方法时此计数器加1。如果计数器为0, acquire()方法将会阻塞,直到其他线程调用re1ease()方法为止。
    • Semaphore([value])
    • 创建一个新的信号量。ⅴa1ve是计数器的初始值。如果省略此参数,计数器的值将被置为l。
    s = threading.Semaphore()
    
    • Semaphore实例s支持以下方法。
    • s.acquire([blocking]):获取信号量。如果进入时内部计数器大于0,此方法将把它的值减1,然后立即返回。如果它的值为0,此方法将阻塞,直到另一个线程调用 release()方法为止。 blocking参数的行为与Lock和 RLock
      对象中描述的相同。
    • s.release()通过将内部计数器的值加1来释放一个信号量。如果计数器为0,而且另一个线程正在等待,该线程将被唤醒。如果有多个线程正在等待,只能从它的acquire(调用返回其中一个。线程释放的顺序并不确定。

    有边界的信号量

    • BoundedSemaphore([value])
    • 创建一个新的信号量。va1ue是计数器的初始值。如果省略此参数,计数器的值将被置为1。
    • BoundedSemaphore的工作方式与 Semaphore完全相同,但 release()操作的次数不能超过 acquire()操作的次数
    • 信号量与互斥锁之间的微妙差别在于:信号量可用于发信号。例如,可以从不同线程调用 acquire()和 release()方法,以便在生产者和消费者线程之间进行通信。

    事件

    • 事件用于在线程之间通信。一个线程发出“事件”信号,一个或多个其他线程等待它。Event实例管理着一个内部标志,可以使用set()方法将它置为rue,或者使用 clear()方法将它重置为 False,wait()方法将阻塞,直到标志为Ture。
    e = threading.Event()
    
    • 创建新的 Event实例,并将内部标志置为Fa1se。Event实例e支持以下方法。
    1. e.is_set():只有当内部标志为True时才返回True。
    2. e.set():将内部标志置为rue。等待它变为True的所有线程都将被唤醒。
    3. e.clear():将内部标志重置为Fase。
    4. e.wait([timeout]):阻塞直到内部标志为rue。如果进入时内部标志为rue,此方法将立即返回。否则,它将阻塞,直到另一个线程调用set()方法将标志置为True,或者直到出现可选的超时。 timeout是一个浮点数,
      用于指定以秒为单位的超时期限。
    5. 尽管 Event对象可用于给其他线程发信号,但不应该使用它们来实现在生产者/消费者问题中十分典型的通知。

    条件变量

    • 当需要线程关注特定的状态变化或事件的发生时将使用这个锁。典型的用法是生产者-消费者问题,其中一个线程生产的数据供另一个线程使用。
    • 使用下面的构造函数可以创建新的 Condition实例:
    • Condition([lock])
    • 创建新的条件变量。lock是可选的Lock或RLock实例。如果未提供lock参数,就会创建新的RLock实例供条件变量使用。
    cv = threading.Condition()
    
    • 条件变量cv支持以下方法。
    1. cv.acquire(*args): 获取底层锁。此方法将调用底层锁上对应的 acquire(*args)方法。
    2. cv.release():释放底层锁。此方法将调用底层锁上对应的 release()方法。
    3. cv.wait([timeout]):等待直到获得通知或出现超时为止。此方法在调用线程已经获取锁之后调用。调用时,将释放底层锁,而且线程将进入睡眠状态,直到另一个线程在条件变量上执行 notify(或 notify110)方法将其唤醒为止。在线程被唤醒之后,线程将重新获取锁,方法也会返回。timeout是浮点数,单位为秒。如果这段时限耗尽,线程将被唤醒,重新获取锁,而控制将被返回
    4. cv.notify([n]):唤醒一个或多个等待此条件变量的线程。此方法只会在调用线程已经获取锁之后调用,而且如果没有正在等待的线程,它就什么也不做。n指定要唤醒的线程数量,默认为1。被唤醒的线程在它们重新获取锁之前不会从wait()调用返回.
    5. cv.notify_all():唤醒所有等待此条件的线程。

    queue

    • queue模块实现了各种多生产者-多消费者队列,可用于在执行的多个线
      程之间安全地交换信息。
    • queue模块定义了3种不同的队列类。

    Queue([maxsize])
    创建一个FFO( first- in first-out,先进先出)队列。 maXsize是队列中可以放入的项的最大数量。
    如果省略 maxsize参数或将它置为0,队列大小将为无穷大。

    Lifoqueue([maxsize])
    创建一个LIFO( last-in first-out,后进先出)队列(也叫栈)。

    Priorityqueue([maxsize])
    创建一个优先级队列,其中项按照优先级从低到高依次排好。使用这种队列时,项应该是(priority,data)形式的元组,其中 priority是一个数字。

    import queue
    q = queue()
    
    • 队列类的实例q具有以下方法。
    1. q.qsize():返回队列的正确大小。因为其他线程可能正在更新队列,此方法返回的数字不完全可靠。
    2. q.empty():如果队列为空,返回True,否则返回Fa1se。
    3. q.full():如果队列已满,返回rue,否则返回 False。
    4. q.put(item [block [,timeout]]):将item放入队列。如果可选参数bock为True(默认值),调用者将被阻塞直到队列中出现可用的空闲位置为止。否则(block为 False),队列满时将引发Full异常。timeout提供可选的超时值,单位为秒。如果出现超时,将引发Full异常。
    5. q.put_nowait(item):等价于q.put(item, False)方法。
    6. q.get([block [,timeout]]):从队列中删除一项,然后返回这个项。如果可选参数blok为rue(默认值),调用者将阻塞,直到队列中出现可用的空闲位置。否则(block为False),队列为空时将引发Empty异常。timeout提供可选的超时值,单位为秒。如果出现超时,将引发Empty异常。
    7. q.get_nowait():等价于get(0)方法
    8. q.task_done():队列中数据的消费者用来指示对于项的处理已经结束。如果使用此方法,那么从队列中删除的每一项都应该调用一次。
    9. q.join():阻塞直到队列中的所有项均被删除和处理为止。一旦为队列中的每一项都调用了一次q. task done()方法,此方法将会直接返回。

    使用队列的线程示例

    import threading
    from queue import Queue
    class WorkerThread(threading.Thread):
            def __init__(self,*args, **kwargs):
                threading.Thread.__init__(self, *args, **kwargs)
                self.input_queue= Queue()
            def send(self, item):
                self.input_queue.put(item)
            def close(self):
                self.input_queue.put(None)
                self.input_queue. oin()
            def run(self):
                while True:
                    item = self.input_queue.get()
                    if item is None:
                        break
                    print(item)
                    self.input_queue.task_done()
                self.input_queue.task_done()
                return
    
    if __name__ == '__main__':
        w = WorkerThread()
        w.start()
        w.send('hello')
        w.send("world")
        w.close()
    
    

    多线程共享全局变量的问题:

    多线程都是在同一个进程中运行的。因此在进程中的全局变量所有线程都是可共享的。这就造成了一个问题,因为线程执行的顺序是无序的。有可能会造成数据错误

    解决方案:锁机制

    为了解决以上使用共享全局变量的问题。threading提供了一个Lock类,这个类可以在某个线程访问某个变量的时候加锁,其他线程此时就不能进来,直到当前线程处理完后,把锁释放了,其他线程才能进来处理。

    秋来凉风起,无限思远人
  • 相关阅读:
    leetcode 122. Best Time to Buy and Sell Stock II
    leetcode 121. Best Time to Buy and Sell Stock
    python 集合(set)和字典(dictionary)的用法解析
    leetcode 53. Maximum Subarray
    leetcode 202. Happy Number
    leetcode 136.Single Number
    leetcode 703. Kth Largest Element in a Stream & c++ priority_queue & minHeap/maxHeap
    [leetcode]1379. Find a Corresponding Node of a Binary Tree in a Clone of That Tree
    正则表达式
    十种排序算法
  • 原文地址:https://www.cnblogs.com/lalavender/p/10463108.html
Copyright © 2020-2023  润新知