2019-11-26 python多线程基础

看文档发现Python是借鉴Java的多线程,学学java还是有用的。

Lock和RLock的区别

  • RLock 叫做可重入锁(reentrant lock),在锁定状态下,必定有线程拥有这把锁, 在未锁定状态下,没有线程拥有该锁。因为线程拥有,才可以继续上锁
  • Lock 不能被线程拥有
  • RLock除了拥有lock 和unlock状态,还拥有the concepts of “owning thread” and “recursion level”
  • 都支持with语句
In [1]: import time
   ...: from threading import Thread, Lock, current_thread
   ...: 
   ...: lock = Lock()
   ...: 
   ...: 
   ...: def xx():
   ...:     lock.acquire()
   ...:     print(current_thread().getName() + " 子线程上锁")
   ...:     while 1:
   ...:         if not lock.locked():
   ...:             print(current_thread().getName() + " 回到子线程")
   ...:             break
   ...: Thread(target=xx).start()
   ...: time.sleep(2)
   ...: print(current_thread().getName() + " 释放锁")
   ...: lock.release()
Thread-335 子线程上锁
MainThread 释放锁
Thread-335 回到子线程

上面看出lock在子线程上锁然后被主线程释放,因此证明Lock是不被线程拥有的

In [3]: import time
   ...: from threading import Thread, Lock, RLock, current_thread
   ...: 
   ...: lock = RLock()
   ...: lock.acquire()
   ...: 
   ...: def xx():
   ...:     lock.release()
   ...:     print(current_thread().getName() + " 子线程上锁")
   ...:     while 1:
   ...:         print(current_thread().getName() + " 回到子线程")
   ...:         break
   ...: 
   ...: 
   ...: Thread(target=xx).start()
   ...: time.sleep(2)
   ...: print(current_thread().getName() + " 释放锁")
Exception in thread Thread-464:
Traceback (most recent call last):
  File "c:\users\zhao\miniconda3\lib\threading.py", line 917, in _bootstrap_inner
    self.run()
  File "c:\users\zhao\miniconda3\lib\threading.py", line 865, in run
    self._target(*self._args, **self._kwargs)
  File "", line 8, in xx
    lock.release()
RuntimeError: cannot release un-acquired lock

上面看出Rlock如果尝试其他线程释放,会报错。

wait/notify 使用

  • 这两个都是用在Condition Objects,需要注意的是Condition Objects需要锁,锁是条件对象的一部分。你可以传入锁也可以默认创建。

  • 锁必须是Lock或RLock,默认使用RLock

  • wait是临时释放锁,并处于等待状态,其他线程可以获得锁。

  • wait 必须再次获得锁才能往下运行

  • notify不释放锁

  • 其他线程获得同一个锁后,调用notify,wait可以去进行锁的竞争

import threading

cv = threading.Condition()

def xx():
    with cv:
        print("xx 1")
        cv.wait()
        print("xx 2")

def vv():
    with cv:
        print("vv 1")
        cv.notify()
        print("vv 2")


k = threading.Thread(target=xx)
v = threading.Thread(target=vv)
k.start()
v.start()

# 第一种情况
# xx 1
# vv 1
# vv 2
# xx 2

# 第二种情况
# vv 1
# vv 2
# xx 1
# 代码等待中

上面代码只有两种方式,当xx函数先执行,是第一种情况,当vv函数先执行,是第二种情况。仔细分析跟上述观点一致。

生产者消费者模式
这段代码是抄自Python标准库Threading源代码中,使用wait/notify实现线程安全的容器

import sys as _sys
from time import sleep as _sleep
from collections import deque as _deque
from threading import RLock, Condition, Thread, currentThread


def _test():
    class BoundedQueue():

        def __init__(self, limit):
            self.mon = RLock()
            self.rc = Condition(self.mon)
            self.wc = Condition(self.mon)
            self.limit = limit
            self.queue = _deque()

        def _note(self, format, *args):
            format = format % args
            name = currentThread().getName()
            format = "%s: %s\n" % (name, format)
            _sys.stderr.write(format)

        def put(self, item):
            self.mon.acquire()
            while len(self.queue) >= self.limit:
                self._note("put(%s): queue full", item)
                print(self.mon._RLock__count)
                self.wc.wait()
                print(self.mon._RLock__count)
            self.queue.append(item)
            self._note("put(%s): appended, length now %d",
                       item, len(self.queue))
            self.rc.notify()
            self.mon.release()

        def get(self):
            self.mon.acquire()
            while not self.queue:
                self._note("get(): queue empty")
                self.rc.wait()
            item = self.queue.popleft()
            self._note("get(): got %s, %d left", item, len(self.queue))
            self.wc.notify()
            self.mon.release()
            return item

    class ProducerThread(Thread):

        def __init__(self, queue, quota):
            Thread.__init__(self, name="Producer")
            self.queue = queue
            self.quota = quota

        def run(self):
            from random import random
            counter = 0
            while counter < self.quota:
                counter = counter + 1
                self.queue.put("%s.%d" % (self.name, counter))
                _sleep(random() * 0.00001)

    class ConsumerThread(Thread):

        def __init__(self, queue, count):
            Thread.__init__(self, name="Consumer")
            self.queue = queue
            self.count = count

        def run(self):
            while self.count > 0:
                item = self.queue.get()
                print item
                self.count = self.count - 1

    NP = 1
    QL = 1
    NI = 5

    Q = BoundedQueue(QL)
    P = []
    for i in range(NP):
        t = ProducerThread(Q, NI)
        t.name = ("Producer-%d" % (i + 1))
        P.append(t)
    C = ConsumerThread(Q, NI * NP)
    for t in P:
        t.start()
        _sleep(0.000002)
    C.start()
    for t in P:
        t.join()
    C.join()


if __name__ == '__main__':
    _test()

你可能感兴趣的:(2019-11-26 python多线程基础)