我正在编写一个简单的线程服务器,它将向所有客户端发送消息。我有一个在发布更改消息后重置的对象,但是我很难确定如何在所有线程都发布更改消息后重置该对象。
为问题添加一些上下文。我正在构建一个多用户Tkinter python应用程序,它连接到远程数据库以检索信息,应用程序需要知道数据何时更改,以便当用户更新数据时,应用程序的所有其他运行实例都将获得更新。据我所知,MySQL不支持异步应用程序更新。我没有在数据库上每隔5秒运行一次查询以查看是否有更改,而是将此代码服务器放在一边,以便它向客户端上的套接字发送消息,表明数据库发生了更改。
主循环只是一个模拟变化的虚拟
这是我的代码:
import socket, threading, time, select, os
class dbMonitor:
isDBAltered = False
def postChange(self):
self.isDBAltered = True
def __str__(self):
return str(self.isDBAltered)
class ThreadedServer(object):
def __init__(self, port,dbMon):
self.port = port
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.sock.setblocking(0)
self.sock.bind((socket.gethostname(), self.port))
self.dbMon = dbMon
def listen(self):
self.sock.listen(100)
read_list = [self.sock]
while True:
read,write,error = select.select(read_list,[],[],1)
for s in read:
if s is self.sock:
client, address = self.sock.accept()
client.settimeout(60)
threading.Thread(target = self.listenToClient, args = (client,address)).start()
def listenToClient(self, client, address):
read_list = [client]
size = 1024
while True:
response = b'Ack'
if self.dbMon.isDBAltered:
response = b'CHANGE'
try:
client.send(response)
except:
client.close()
return False
self.dbMon.isDBAltered = False
read,write,error = select.select(read_list,[],[],1)
for s in read:
if s is client:
try:
data = client.recv(size)
print(data)
if data:
client.send(response)
else:
raise error('Client disconnected')
except:
client.close()
return False
def mainLoop():
while True:
time.sleep(15)
print(dbMon)
dbMon.postChange()
dbMon = dbMonitor()
server = ThreadedServer(5005,dbMon)
threading.Thread(target = mainLoop, args=()).start()
threading.Thread(target = server.listen(), args=()).start()
如何在所有线程执行完毕后才能执行self.dbMon.isDBAltered = False
:
response = b'CHANGE'
try:
client.send(response)
答案 0 :(得分:0)
你正试图同步异步的东西......这比应该的要复杂得多。你的dbmon只存储一个布尔标志...为什么不只是异步修改“数据库”呢?例如,如果“数据库”是一个线程安全缓冲区,您可以只是附加到该缓冲区或修改该缓冲区而不单独同步每个线程,将写入该缓冲区的信息写入它们所属的客户端套接字。另一个事件循环(这几乎是asyncore所做的)
那就是说,我有一些(可能是非工作,但我希望你能得到这个想法)参考修改后的代码,如果你想继续追求这条道路,你就可以了。
基本上,dbmon将保持线程ID到[创建时间,修改标志]
的映射如果在某个阈值之前创建的所有线程都设置了修改后的标志,则我们的谓词返回true。当我们在代码的data = client.recv(size)部分发送响应时,我们设置了修改标志。然后我们在服务器发送中等待那个条件。我们不断通知每个客户端接收的所有等待线程,以便在最终满足条件时,我们的等待服务器线程将全部解除阻塞并发送后续响应。
import socket, threading, time, select, os
import collections
class dbMonitor:
def __init__(self):
self.isDBAltered = {}
self.lock = threading.Lock()
def newThread(self, tid):
self.lock.acquire()
# time of creation, boolean whether that thread has sent response
self.isDBAltered[tid] = [time.time(), False]
self.lock.release()
def threadDone(self, tid):
self.lock.acquire()
self.isDBAltered.pop(tid, None)
self.lock.release()
def altered(self, tid):
self.lock.acquire()
self.isDBAltered[tid][1] = True
self.lock.release()
def reset(self, tid):
self.lock.acquire()
self.isDBAltered(tid)[1] = False
self.lock.release()
def __str__(self):
return str(self.isDBAltered)
class ThreadedServer(object):
def __init__(self, port,dbMon):
self.port = port
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.sock.setblocking(0)
self.sock.bind((socket.gethostname(), self.port))
self.dbMon = dbMon
self.lock = threading.lock()
self.cv = threading.Condition()
self.thresh = 2000
def condition_pred(self):
# unblock if threads currently running for longer than self.thresh have all set their flags
return all([timecreate[1] if time.time() - timecreate[0] > self.thresh else True for tid,timecreate in self.dbMon.isDBAltered])
def listen(self):
self.sock.listen(100)
read_list = [self.sock]
while True:
read,write,error = select.select(read_list,[],[],1)
for s in read:
if s is self.sock:
self.lock.acquire()
client, address = self.sock.accept()
client.settimeout(60)
T = threading.Thread(target = self.listenToClient, args = (client,address)).start()
self.dbmon.newThread(T.ident)
self.lock.release()
def listenToClient(self, client, address):
read_list = [client]
size = 1024
while True:
response = b'Ack'
with self.cv:
self.cv.wait_for(self.condition_pred)
self.dbMon.reset(threading.get_ident())
response = b'CHANGE'
try:
client.send(response)
except:
client.close()
self.dbmon.threadDone(threading.get_ident())
return False
read,write,error = select.select(read_list,[],[],1)
for s in read:
if s is client:
with self.cv:
try:
data = client.recv(size)
print(data)
if data:
client.send(response)
self.dbMon.altered(threading.get_ident())
self.cv.notifyAll()
else:
raise error('Client disconnected')
except:
client.close()
self.dbmon.threadDone(threading.get_ident())
return False