我正在尝试将我的工作并行化,但我是多线程的新手,所以对具体的实现感到困惑。
我有一个套接字侦听器,它将数据保存到缓冲区。当缓冲区达到其容量时,我需要将其数据保存到数据库。 在一个线程上我想启动套接字侦听器,而在并行任务上我想检查缓冲区状态。
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
如果你能帮我解决这个问题,我很感激
答案 0 :(得分:3)
我认为您所需要的只是queue
模块。
queue.Queue
是一个自同步队列,专门用于在线程之间传递对象。
默认情况下,在队列上调用get
会阻塞,直到对象可用为止,这通常是您想要做的-在网络应用程序中使用线程进行并发的要点是,您的线程看起来都像正常的同步代码,但是将大部分时间都花在等待套接字,文件,队列或其他无关的事情上。但是您可以使用block=False
进行检查而不会阻塞,或者将timeout
置于等待状态。
构造队列时,还可以指定maxsize
。然后,默认情况下,put
将阻塞,直到队列不太满而无法接受新对象。但是,再次,您可以使用block
或timeout
尝试,如果太满了,就会失败。
所有同步都在get
和put
内部进行,因此您不需要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 - 选择也适用于套接字。您可以传递套接字对象而不是缓冲区,它会检查套接字上的缓冲区是否已准备好进行读取。