首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么我在使用threading.Lock和'with‘时会出现线程匮乏?(Python 3.6.6)

为什么我在使用threading.Lock和'with‘时会出现线程匮乏?(Python 3.6.6)
EN

Stack Overflow用户
提问于 2018-10-15 02:07:13
回答 1查看 190关注 0票数 0

tl;dr

使用threading.Lock和with会导致线程匮乏(一个线程完成全部或大部分工作):

代码语言:javascript
复制
with lock:
    next(generator)

但是手动调用acquire()和release()可以在线程之间更均匀地分配工作:

代码语言:javascript
复制
lock.acquire()
next(generator)
lock.release()

完整的代码和输出示例

使用threading.Lock()和with进行同步

代码语言:javascript
复制
import threading
import queue
import time
import random

class ThreadedGen:
  def __init__(self, generator):
    self._generator = generator
    self._queue = queue.Queue(10)
    self._lock  = threading.Lock()

  def _run(self):
    while True:
      with self._lock: # <==== use with, no explicit call to acuire()/release()
        try:
          # get the next item from the generator
          # put the thread name and item as a tuple on the queue
          n = next(self._generator)
          self._queue.put((threading.current_thread().name, n))
        except Exception as e:
          print(threading.current_thread().name, e)

  def start(self):
    for _ in range(3): # spawn three threads
      threading.Thread(target=self._run).start()

  def get(self):
    while True:
      yield self._queue.get()

if __name__ == '__main__':
  def gen():
    n = 0
    while n < 100:
      s = random.randint(0,5)
      time.sleep(s) # simulate work
      yield n
      n += 1

  t = ThreadedGen(gen())
  t.start()
  outputgen = t.get()
  for _ in range(100):
    (tname, n) = next(outputgen)
    print(f'got {n} from {tname}')

输出:

代码语言:javascript
复制
got 0 from Thread-1
got 1 from Thread-1
got 2 from Thread-1
got 3 from Thread-1
got 4 from Thread-1
got 5 from Thread-1
got 6 from Thread-1
got 7 from Thread-1
got 8 from Thread-1
got 9 from Thread-1
got 10 from Thread-1
got 11 from Thread-1
got 12 from Thread-1
got 13 from Thread-1
got 14 from Thread-1
got 15 from Thread-1
got 16 from Thread-1
got 17 from Thread-1
got 18 from Thread-1
got 19 from Thread-1
got 20 from Thread-1
got 21 from Thread-1
got 22 from Thread-1
got 23 from Thread-1
got 24 from Thread-1
got 25 from Thread-1
got 26 from Thread-1
got 27 from Thread-1
got 28 from Thread-1
got 29 from Thread-1
got 30 from Thread-1
got 31 from Thread-1
got 32 from Thread-1
got 33 from Thread-1
got 34 from Thread-1
got 35 from Thread-1
got 36 from Thread-1
got 37 from Thread-1
got 38 from Thread-1
got 39 from Thread-1
got 40 from Thread-1
got 41 from Thread-1
got 42 from Thread-1
got 43 from Thread-1
got 44 from Thread-1
got 45 from Thread-1
got 46 from Thread-1
got 47 from Thread-3
got 48 from Thread-3
got 49 from Thread-3
got 50 from Thread-3
...

使用threading.Lock()和显式acquire() release()进行同步

代码语言:javascript
复制
import threading
import queue
import time
import random

class ThreadedGen:
  def __init__(self, generator):
    self._generator = generator
    self._queue = queue.Queue(10)
    self._lock  = threading.Lock()

  def _run(self):
    while True:
      try:
        # get the next item from the generator
        # put the thread name and item as a tuple on the queue
        self._lock.acquire() # <==== explicit acquire() call
        n = next(self._generator)
        self._lock.release() # <==== explicit release() call
        self._queue.put((threading.current_thread().name, n))
      except Exception as e:
        self._lock.release()
        print(threading.current_thread().name, e)

  def start(self):
    for _ in range(3): # spawn three threads
      threading.Thread(target=self._run).start()

  def get(self):
    while True:
      yield self._queue.get()

if __name__ == '__main__':
  def gen():
    n = 0
    while n < 100:
      s = random.randint(0,5)
      time.sleep(s) # simulate work
      yield n
      n += 1

  t = ThreadedGen(gen())
  t.start()
  outputgen = t.get()
  for _ in range(100):
    (tname, n) = next(outputgen)
    print(f'got {n} from {tname}')

输出:

代码语言:javascript
复制
got 0 from Thread-1
got 1 from Thread-2
got 2 from Thread-3
got 3 from Thread-1
got 4 from Thread-2
got 5 from Thread-3
got 6 from Thread-1
got 7 from Thread-2
got 8 from Thread-3
got 9 from Thread-1
got 10 from Thread-1
got 11 from Thread-3
got 12 from Thread-2
got 13 from Thread-1
got 14 from Thread-3
got 15 from Thread-2
got 16 from Thread-1
got 17 from Thread-3
got 18 from Thread-2
got 19 from Thread-1
got 20 from Thread-3
got 21 from Thread-2
got 22 from Thread-1
got 23 from Thread-3
got 24 from Thread-2
got 25 from Thread-1
got 26 from Thread-3
got 27 from Thread-2
got 28 from Thread-1
got 29 from Thread-3
got 30 from Thread-2
got 31 from Thread-1
got 32 from Thread-3
got 33 from Thread-2
got 34 from Thread-2
got 35 from Thread-2
got 36 from Thread-3
got 37 from Thread-1
got 38 from Thread-2
got 39 from Thread-3
got 40 from Thread-1
got 41 from Thread-2
got 42 from Thread-2
got 43 from Thread-1
got 44 from Thread-3
got 45 from Thread-2
got 46 from Thread-1
got 47 from Thread-3
got 48 from Thread-2
got 49 from Thread-1
got 50 from Thread-1
EN

回答 1

Stack Overflow用户

发布于 2018-10-15 02:07:13

当我输入我的问题时,我想出了这个问题(非常感谢!)问题是,在调用next(self._generator)之前,我并没有将with放在同一位置

错误:

代码语言:javascript
复制
  with self._lock:
    try:
      # get the next item from the generator
      # put the thread name and item as a tuple on the queue
      n = next(self._generator)
      self._queue.put((threading.current_thread().name, n))
    except Exception as e:
      print(threading.current_thread().name, e)

右图:

代码语言:javascript
复制
    try:
      # get the next item from the generator
      # put the thread name and item as a tuple on the queue
      with self._lock:
        n = next(self._generator)
      self._queue.put((threading.current_thread().name, n))
    except Exception as e:
      print(threading.current_thread().name, e)

如果队列已满,则默认情况下queue.put()会阻塞。这意味着锁不会被立即释放,从而阻止其他线程访问生成器,并在第一个线程可以重新获取锁之前竞相从第一个线程获取锁(所以这是一个竞争条件)。

摘要:

with尽可能靠近同步的调用,以防止其他阻塞调用延迟其他线程对资源的访问

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/52805604

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档