一个生产者线程和多个非阻塞同时消费者线程(MROW)python

时间:2014-02-27 20:22:01

标签: python multithreading synchronization nonblocking

我有一个生产者线程读取一些数据并处理它们和其他线程读取,我在生产者中使用条件变量以避免繁忙等待并在新的数据采集准备好时通知,但每个消费者当他们想要读取数据时接受锁定,以便其他消费者线程必须等待,当生产者完成时,让所有消费者立即读取数据的更好方法是什么?,

以下是一些代码:

制作人:

condition = threading.Condition()


def get_data():
while(1):
    global actumulated_data, sdev1, mean1, number_of_adq, instant_data
    time.sleep(1.3660619/1000000.0)
    number_of_adq = number_of_adq + 1
    fpga.write_int('adc_data_ctrl',0)
    fpga.write_int('adc_data_ctrl',7)
    data = fpga.read('adc_data_bram',8192,0)
    data2 = map(hex, map(ord, data))
    for i in range(8192):
        data2[i] = fix8_7(twos_comp(int(data2[i],16),8))
    condition.acquire() # condition variable
    instant_data = numpy.array(data2)
    actumulated_data = numpy.concatenate((actumulated_data,instant_data),axis=0)    
    condition.notifyAll()
    condition.release()

其中一位消费者:

def plot_adc_out():
while(1):
    condition.acquire()
    plt.clf()
    plt.plot(instant_data)
    plt.grid(True)
    plt.xlim(0, len(instant_data))
    plt.ylim(-1.5,1.5)
    plt.draw()
    condition.wait()
    condition.release() 

所以有一些消费者喜欢这个,我正在考虑像C ++这样的带有读写锁定的东西,其中如果锁是写锁定会阻塞但是如果读取锁是非阻塞的那么可以是很多线程同时读取该项目。

Readers–writer lock

pthread_rwlock_wrlock
pthread_rwlock_rdlock

但我相信python没有读写锁。

3 个答案:

答案 0 :(得分:1)

线程进行通信的正确方法是使用锁,但queue.Queue数据结构为线程间通信提供了更方便的机制,所有丑陋的锁定内容都可以隐藏在封面下。这是一个改编自我的O'Reilly技术学院课程的例子,它通过将每个字符连接到一个单独的线程来将字符串转换为大写字母。请注意下面的代码是Python 3,但很容易适应Python 2。

一切都由控制线程启动(它从各自的模块中导入下面显示的输出和工作线程)。它首先创建输入和输出队列。这些是标准的FIFO,比工作线程的数量限制多50%,以避免在缓冲对象中锁定太多内存。然后它创建并启动输出线程,最后创建并启动WORKERS常量配置的工作线程数。工作线程从输入队列中获取并放入输出队列。

"""
control.py: Creates queues, starts output and worker threads,
            and pushes inputs into the input queue.
"""
from queue import Queue
from output import OutThread
from worker import WorkerThread

WORKERS = 10

inq = Queue(maxsize=int(WORKERS*1.5))
outq = Queue(maxsize=int(WORKERS*1.5))

ot = OutThread(WORKERS, outq)
ot.start()

for i in range(WORKERS):
    w = WorkerThread(inq, outq)
    w.start()
instring = input("Words of wisdom: ")
for work in enumerate(instring):
    inq.put(work)
for i in range(WORKERS):
    inq.put(None)
inq.join()
print("Control thread terminating")

已经投射了工作线程,以便简化交互。从输入队列接收的工作单元是(索引,字符)对,输出单元也是对。处理被拆分为一个单独的方法,使子类化更容易 - 简单地覆盖process()方法。

"""
worker.py: a sample worker thread that receives input
           through one Queue and routes output through another.
"""
from threading import Thread

class WorkerThread(Thread):
    def __init__(self, iq, oq, *args, **kw):
        """Initialize thread and save Queue references."""
        Thread.__init__(self, *args, **kw)
        self.iq, self.oq = iq, oq
    def run(self):
        while True:
            work = self.iq.get()
            if work is None:
                self.oq.put(None)
                print("Worker", self.name, "done")
                self.iq.task_done()
                break
            i, c = work
            result = (i, self.process(c)) # this is the "work"
            self.oq.put(result)
            self.iq.task_done()
    def process(self, s):
        """This defines how the string is processed to produce a result"""
        return s.upper()

输出线程只需从工作线程放置它们的队列中提取输出数据包。当每个工作线程终止时,它会向队列中发布None。当从每个线程收到None时,输出线程终止。输出线程在初始化时被告知有多少个工作线程,每次接收到另一个None时,它会减少工作器数量,直到最终没有工人离开。此时,输出线程终止。由于不保证工作线程以任何特定顺序返回,因此可以对结果进行排序。如果没有排序,您可以看到结果到达的顺序。

"""
output.py: The output thread for the miniature framework.
"""
identity = lambda x: x

import threading
class OutThread(threading.Thread):
    def __init__(self, N, q, sorting=True, *args, **kw):
        """Initialize thread and save queue reference."""
        threading.Thread.__init__(self, *args, **kw)
        self.queue = q
        self.workers = N
        self.sorting = sorting
        self.output = []
    def run(self):
        """Extract items from the output queue and print until all done."""
        while self.workers:
            p = self.queue.get()
            if p is None:
                self.workers -= 1
            else:
                # This is a real output packet
                self.output.append(p)
        print("".join(c for (i, c) in (sorted if self.sorting else identity)(self.output)))
        print ("Output thread terminating"

答案 1 :(得分:0)

如果您最终使用锁定任何东西,我建议使用'使用'而不是明确地获取和释放..

with self._some_lock:
    # Do some stuff that accesses a shared resource
    self._shared_resource = some_value

那将为你做所有丑陋的事情,并且具有缩进锁定代码的优势,这样就可以更难做出简单的错误(比如在释放锁等之前返回......)

答案 2 :(得分:0)

以下是ReadWriteLock的python(3+)实现,可解决编写者的饥饿问题,并支持在构建期间按需将读取锁升级为写入锁。它仅使用一个锁和一个条件。

使用此Lock类和WriteRWLock上下文管理器类重写Producer-

rwLock = ReadWriteLock()

def get_data():
while(1):
    global actumulated_data, sdev1, mean1, number_of_adq, instant_data
    time.sleep(1.3660619/1000000.0)
    number_of_adq = number_of_adq + 1
    fpga.write_int('adc_data_ctrl',0)
    fpga.write_int('adc_data_ctrl',7)
    data = fpga.read('adc_data_bram',8192,0)
    data2 = map(hex, map(ord, data))
    for i in range(8192):
        data2[i] = fix8_7(twos_comp(int(data2[i],16),8))
    with WriteRWLock(rwLock):  # Guard object acquiring a Write Lock
        instant_data = numpy.array(data2)
        actumulated_data = numpy.concatenate((actumulated_data,instant_data),axis=0)    

使用ReadRWLock上下文管理器类重写使用者-

def plot_adc_out():
while(1):
    with ReadRWLock(rwLock):    # Guard object acquiring a Read Lock
        plt.clf()
        plt.plot(instant_data)
        plt.grid(True)
        plt.xlim(0, len(instant_data))
        plt.ylim(-1.5,1.5)
        plt.draw()

使用上述代码,您可以有多个使用者并行使用Instant_data。

完整的ReadWriteLock类和必需的上下文管理器。

# From O'Reilly Python Cookbook by David Ascher, Alex Martelli
# With changes to cover the starvation situation where a continuous
#   stream of readers may starve a writer, Lock Promotion and Context Managers

class ReadWriteLock:
  """ A lock object that allows many simultaneous "read locks", but
  only one "write lock." """

  def __init__(self, withPromotion=False):
    self._read_ready = threading.Condition(threading.RLock(  ))
    self._readers = 0
    self._writers = 0
    self._promote = withPromotion
    self._readerList = []  # List of Reader thread IDs
    self._writerList = []  # List of Writer thread IDs

  def acquire_read(self):
    logging.debug("RWL : acquire_read()")
    """ Acquire a read lock. Blocks only if a thread has
    acquired the write lock. """
    self._read_ready.acquire(  )
    try:
      while self._writers > 0:
        self._read_ready.wait()
      self._readers += 1
    finally:
      self._readerList.append(threading.get_ident())
      self._read_ready.release(  )

  def release_read(self):
    logging.debug("RWL : release_read()")
    """ Release a read lock. """
    self._read_ready.acquire(  )
    try:
      self._readers -= 1
      if not self._readers:
        self._read_ready.notifyAll(  )
    finally:
      self._readerList.remove(threading.get_ident())
      self._read_ready.release(  )

  def acquire_write(self):
    logging.debug("RWL : acquire_write()")
    """ Acquire a write lock. Blocks until there are no
    acquired read or write locks. """
    self._read_ready.acquire(  )   # A re-entrant lock lets a thread re-acquire the lock
    self._writers += 1
    self._writerList.append(threading.get_ident())
    while self._readers > 0:
      # promote to write lock, only if all the readers are trying to promote to writer
      # If there are other reader threads, then wait till they complete reading
      if self._promote and threading.get_ident() in self._readerList and set(self._readerList).issubset(set(self._writerList)):
        break
      else:
        self._read_ready.wait(  )

  def release_write(self):
    logging.debug("RWL : release_write()")
    """ Release a write lock. """
    self._writers -= 1
    self._writerList.remove(threading.get_ident())
    self._read_ready.notifyAll(  )
    self._read_ready.release(  )

#----------------------------------------------------------------------------------------------------------

class ReadRWLock:
  # Context Manager class for ReadWriteLock
  def __init__(self, rwLock):
    self.rwLock = rwLock

  def __enter__(self):
    self.rwLock.acquire_read()
    return self         # Not mandatory, but returning to be safe

  def __exit__(self, exc_type, exc_value, traceback):
    self.rwLock.release_read()
    return False        # Raise the exception, if exited due to an exception

#----------------------------------------------------------------------------------------------------------

class WriteRWLock:
  # Context Manager class for ReadWriteLock
  def __init__(self, rwLock):
    self.rwLock = rwLock

  def __enter__(self):
    self.rwLock.acquire_write()
    return self         # Not mandatory, but returning to be safe

  def __exit__(self, exc_type, exc_value, traceback):
    self.rwLock.release_write()
    return False        # Raise the exception, if exited due to an exception

#----------------------------------------------------------------------------------------------------------