python线程同步

Posted kmnskd

tags:

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

线程同步

  • 同步:任意数量的线程可以访问临界区的代码,但在给定的时刻又只有一个线程可以通过时。

  • 在使用线程的时候一个很重要的问题就是避免多个线程对同一变量或其他资源的访问冲突。重叠访问、在多个线程中修改等这些操作会导致各种各样的问题。

  • 锁是python的threading提供的最基本的同步机制,在任一时刻,一个锁对象只能被一个线程获取,或者不被任何线程获取。如果一个线程去获取一个锁对象只能等待这个锁对象被另一个线程释放。

  • Lock

lock = Lock()
lock.acquire()#获得锁对象
lock.release()#释放锁
#也可以使用with加锁
def func(a, lock):
    lock.acquire()
    for var in range(5):
        a += 3
        print(a)
    lock.release()
def work(a, lock):
    lock.acquire()
    for var in range(5):
        a += 1
        print(a)
    lock.release()
def main():
    a = 1
    lock = Lock()
    t1 = Thread(target=work, args=(a, lock))
    t2 = Thread(target=func, args=(a, lock))
    t1.start()
    t2.start()
    t1.join()
    t2.join()
if __name__ == __main__:
    main()
  • 为每个共享资源创建一个Lock对象,当要访问该资源需要调用acquire来获取锁对象,如果其他线程已经获得锁,那么当前线程的等待锁释放,待资源访问完调用release释放锁。

Semaphores
  • 信号量是一个更高级的锁机制。信号量内部有一个计数器而不像锁对象内部有锁标识,而且只有当占用信号量的线程数超过信号量时线程才阻塞。这允许了多个线程可以同时访问相同的代码区。

  • Semaphore是计数信号量。Semaphore管理一系列许可证。每个acquire方法阻塞,直到有一个许可证可以获得然后拿走一个许可证;每个release方法增加一个许可证,这可能会释放一个阻塞的acquire方法。然而,其实并没有实际的许可证这个对象,Semaphore只是维持了一个可获得许可证的数量。

  • acquire(blocking=True,timeout=None)

    • 本方法用于获取 Semaphore

    • 当使用默认参数调用本方法时:如果内部计数器的值大于零,将之减一,并返回;如果等于零,则阻塞,并等待其他线程调用 release() 方法以使计数器为正。这个过程有严格的互锁机制控制,以保证如果有多条线程正在等待解锁,release() 调用只会唤醒其中一条线程。唤醒哪一条是随机的。本方法返回 True,或无限阻塞

    • 如果 blocking=False,则不阻塞,但若获取失败的话,返回 False

    • 当设定了 timeout 参数时,最多阻塞 timeout 秒,如果超时,返回 False

  • release()

    • 释放 Semaphore,给内部计数器 +1,可以唤醒处于等待状态的线程。

import threading
import time
?
def fun(semaphore, num):
    # 获得信号量,信号量减一
    semaphore.acquire()
    print "Thread %d is running." % num
    time.sleep(3)
    # 释放信号量,信号量加一
    semaphore.release()
?
if __name__==__main__:
    # 初始化信号量,数量为2
    semaphore = threading.Semaphore(2)
?
    # 运行4个线程
    for num in xrange(4):
        t = threading.Thread(target=fun, args=(semaphore, num))
        t.start()
        
Thread 0 is running. 
Thread 1 is running. 
#3秒之后打印
Thread 2 is running. 
Thread 3 is running.
BoundedSemaphore 最大可执行线程
  • threading.BoundedSemaphore(5)设置可同时执行的最大线程数为5个,后面的线程需排队等待前面的线程执行完毕 。

    import time,threading
    def runtask(name):
      global num
      semaphore.acquire()
      time.sleep(1)
      num += 1
      semaphore.release()
      print(name,num)
    num = 0
    semaphore = threading.BoundedSemaphore(5)
    for index in range(50):
      t = threading.Thread(target=runtask,args=("线程%s"%index,))
      t.start()
Event
  • 信号状态,一个内部标识,线程等待这个标识被其他线程活自己设定、清除这个标识。

    
    
    event = Event()
    event.wait()#在内部设定
    event.set()#设定标识,非阻塞
    event.clear()#清除标识,阻塞

Rlock

  • 递归锁

    def run1():
      global count1
      lock.acquire()
      count1 += 1
      lock.release()
      return count1
    def run2():
      global count2
      lock.acquire()
      count2 += 1
      lock.release()
      return count2
    def runtask():
      lock.acquire()
      r1 = run1()
      print("="*30)
      r2 = run2()
      lock.release()
      print(r1,r2)
    count1,count2 = 0,0
    lock = threading.Lock()
    for index in range(50):
      t = threading.Thread(target=runtask,)
      t.start()
    • 这是一个很简单的线程锁死案例,程序将被卡死,停止不动。为了解决这一情况,Python提供了递归锁RLock(可重入锁)。这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。

    • lock=threading.Lock() 改为lock = threading.RLock()

Queue
  • 线程队列,也叫生产消费者模型。

  • from Queue import queue

    • q = queue(maxsize=0) maxsize指定队列长度,默认为0标识队列无限制。

    • q.put(item,block=True,timeout=None) item必须存在,block为True时阻塞,False为非阻塞。== q.put__nowait()

    • q.get(block=True,timeout=None) block为True时阻塞,False非为阻塞。== q.get__nowait()

    • q.qsize() 返回队列大小

    • q.empty() 当队列为空的时候,返回True 否则返回False (不可靠 )

    • q.full()    当队列满的时候,返回True,否则返回False (不可靠)

    from threading import Thread
    from queue import Queue
    def work(q):
        for var in range(10):
            num = q.get()
            print(num)
    if __name__ == __main__:
        q = Queue(20)
        for var in range(10):
            q.put(var,block=False)
        t = Thread(target=work,args=(q,))
        t.start()
        t.join()
    0
    1
    2
    3
    4
    5
    6
    7
    8
    9

     

 

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

起底多线程同步锁(iOS)

python多线程编程—同步原语入门(锁Lock信号量(Bounded)Semaphore)

python线程同步

Python线程同步

多线程编程

Python多线程同步