带队列的Python线程:如何避免使用join?

时间:2014-12-11 08:54:51

标签: python linux multithreading queue can-bus

我有一个2线程的场景:

  1. 等待来自套接字的消息的线程(嵌入在C库中 - 阻塞调用是“Barra.ricevi”)然后将一个元素放在队列上

  2. 线程等待从队列中获取元素并执行某些操作

  3. 示例代码

    import Barra
    import Queue    
    import threading
    
    posQu = Queue.Queue(maxsize=0)
    
    def threadCAN():
        while True:
            canMsg = Barra.ricevi("can0")
            if canMsg[0] == 'ERR':
                print (canMsg)
            else:
                print ("Enqueued message"), canMsg
                posQu.put(canMsg)
    
    thCan = threading.Thread(target = threadCAN)
    thCan.daemon = True
    thCan.start()
    
    while True:
        posMsg = posQu.get()
        print ("Messagge from the queue"), posMsg
    

    结果是每次来自套接字的新消息都会向队列中添加一个新元素,但是应该从队列中获取项目的主线程永远不会被唤醒。

    输出如下:

      

    排队留言

         

    排队留言

         

    排队留言

         

    排队留言

    我希望有:

      

    排队留言

         

    队列中的消息

         

    排队留言

         

    队列中的消息

    解决此问题的唯一方法是添加行:

    posQu.join()
    

    在线程结束时等待来自套接字的消息,以及行:

    posQu.task_done()
    

    在主线程的末尾。

    在这种情况下,在从套接字收到新消息之后,线程阻塞等待主线程处理排队的项目。

    不幸的是,这不是理想的行为,因为我希望线程始终准备从套接字获取消息而不是等待从另一个线程补充作业。

    我做错了什么? 感谢

    安德鲁   (意大利)

1 个答案:

答案 0 :(得分:0)

这可能是因为BarraBarra.ricevi时没有释放全局解释器锁(GIL)。你可能想检查一下。

GIL确保一次只能运行一个线程(限制多处理器系统中线程的有用性)。 GIL每隔100个“滴答”切换一次线程 - 松散地映射到字节码指令。有关详细信息,请参阅here

在你的生产者线程中,在C库调用之外没有太多事情发生。这意味着生产者线程将在GIL切换到另一个线程之前多次调用Barra.ricevi

对此的解决方案是增加复杂性:

  • 将项目添加到队列后调用time.sleep(0)。这会生成线程,以便另一个线程可以运行。
  • 使用sys.setcheckinterval()降低切换线程之前执行的“滴答”量。这将以使程序计算成本更高为代价。
  • 使用multiprocessing而不是threading。这包括使用multiprocessing.Queue代替Queue.Queue
  • 修改Barra,以便在调用其函数时释放GIL。

使用multiprocessing的示例。请注意,使用多处理时,您的进程不再具有隐含的共享状态。您需要查看多处理,以了解如何在进程之间传递信息。

import Barra  
import multiprocessing

def threadCAN(posQu):
    while True:
        canMsg = Barra.ricevi("can0")
        if canMsg[0] == 'ERR':
            print(canMsg)
        else:
            print("Enqueued message", canMsg)
            posQu.put(canMsg)

if __name__ == "__main__":
    posQu = multiprocessing.Queue(maxsize=0)
    procCan = multiprocessing.Process(target=threadCAN, args=(posQu,))
    procCan.daemon = True
    procCan.start()

    while True:
        posMsg = posQu.get()
        print("Messagge from the queue", posMsg)