具有共享变量的Python多线程

时间:2018-06-19 07:33:09

标签: python multithreading sockets websocket multiprocessing

我正在尝试将我的工作并行化,但我是多线程的新手,所以对具体的实现感到困惑。

我有一个套接字侦听器,它将数据保存到缓冲区。当缓冲区达到其容量时,我需要将其数据保存到数据库。 在一个线程上我想启动套接字侦听器,而在并行任务上我想检查缓冲区状态。

BufferQueue只是python list的扩展,其方法允许检查列表是否已达到指定的大小。

SocketManager是我正在收听的STREAM_URL的流数据提供商。它使用回调函数来处理消息

但是当我使用回调来检索数据时,我不确定使用共享变量是否是正确的最佳决策

buffer = BufferQueue(buffer_size=10000)

def start_listening_to_sokcet(client):
    s = SocketManager(client)
    s.start_socket(cb_new)
    s.start()

def cb_new(message):
    print("New message")
    global buffer
    for m in message:
        #save data to buffer

def is_buffer_ready(buffer):
    global buffer
    print("Buffer state")
    if buffer.ready():
         #save buffer data to db

如果你能帮我解决这个问题,我很感激

2 个答案:

答案 0 :(得分:3)

我认为您所需要的只是queue模块。

queue.Queue是一个自同步队列,专门用于在线程之间传递对象。

默认情况下,在队列上调用get会阻塞,直到对象可用为止,这通常是您想要做的-在网络应用程序中使用线程进行并发的要点是,您的线程看起来都像正常的同步代码,但是将大部分时间都花在等待套接字,文件,队列或其他无关的事情上。但是您可以使用block=False进行检查而不会阻塞,或者将timeout置于等待状态。

构造队列时,还可以指定maxsize。然后,默认情况下,put将阻塞,直到队列不太满而无法接受新对象。但是,再次,您可以使用blocktimeout尝试,如果太满了,就会失败。

所有同步都在getput内部进行,因此您不需要Lock来保证线程安全,也不需要Condition来向服务员发信号。

队列甚至可以为您解决关机问题。生产者可以只是put的特殊值,当消费者在get上看到它时,它告诉消费者退出。

要进行正常关机,在这种情况下,生产者需要等到使用者完成后,可以在使用者完成对每个排队对象的处理之后使用可选的task_done方法,并将生产者块放在{{1 }} 方法。但是,如果您不需要此功能,或者有其他等待关闭的方式(例如,加入使用者线程),则可以跳过这一部分。

答案 1 :(得分:0)

多线程为您提供共享的资源状态(变量)。不要使用全局变量,只需将缓冲区作为参数传递给方法,然后从/向它读取/写入。

您仍然需要控制对缓冲区资源的访问,因此两个线程不会同时读/写。您可以使用Lock模块中的threading来实现此目的:

lock = threading.Lock()

def cb_new(buffer_, lock_, message):
    print("New message")
    with lock_():
        for m in message:
            #save data to buffer
            buffer.add(m)

def is_buffer_ready(buffer_, lock_):
    print("Buffer state")
    with lock_():
        if buffer_.ready():
             #save buffer data to db

请注意,如果您使用多处理而不是线程,此解决方案将无法正常工作。

顺便说一下,正如@abarnert评论的那样,有更好的机制来检查缓冲区是否就绪(有数据要读/有可写空间)然后调用一个检查它的函数。查看阻止你的select.select(),直到缓冲区实际上准备好

使用select时,将调用置于while True循环内,然后检查缓冲区是否已准备好进行读取。您可以在线程中启动此函数,传递标志变量和缓冲区。如果要停止线程,请将传递的标志更改为False。对于缓冲区对象,请使用Queue.Queue()或类似的数据结构。

def read_select(flag, buff):
    flag = 1
    while flag:
        r, _, _ = select.select([buff], [], [])
        if r:
            data = s.read(BUFFSIZE)
            # process data

P.S - 选择也适用于套接字。您可以传递套接字对象而不是缓冲区,它会检查套接字上的缓冲区是否已准备好进行读取。