我需要服务器在某些时候刷新一些数据。这是通过使用Cx_Oracle从Oracle DB中提取行来完成的。
感谢Tornado的PeriodicCallback,如果要加载新数据,程序会每30秒检查一次:
server.start()
from tornado.ioloop import PeriodicCallback
pcallback = PeriodicCallback(db_obj.reload_data_async, 10 * 1e3)
pcallback.start()
server.io_loop.start()
其中db_obj
是一个类的实例,它负责DB相关的函数(connect,fetch,...)。
基本上,这就是reload_data_async
函数的样子:
executor = concurrent.futures.ThreadPoolExecutor(4)
# methods of the db_obj class ...
@gen.coroutine
def reload_data_async(self):
# ... first, some code to check if the data should be reloaded ...
# ...
if data_should_be_reloaded:
new_data = yield executor.submit(self.fetch_data)
def fetch_data(self):
""" fetch new data in the DB """
cursor = cx.Cursor(self.db_connection)
cursor.execute("some SQL select request that takes time (select * from ...)")
rows = cursor.fetchall()
# some more processing thereafter
# ...
基本上,这是有效的。但是当我在fetch_data
加载数据时尝试读取数据(通过点击显示在GUI中),程序因竞争条件而崩溃(我猜?):它在获取数据时访问数据同时。
我刚刚发现tornado.concurrent.futures不是线程安全的:
tornado.concurrent.Future类似于concurrent.futures.Future,但是 不是线程安全的(因此使用单线程更快 事件循环)。
总而言之,我认为我应该创建一个新线程来处理CX_Oracle操作。我可以使用Tornado执行此操作并继续使用PerodicCallback
功能吗?如何将我的异步操作转换为线程安全?有什么办法呢?
PS:我正在使用Python 2.7
由于
答案 0 :(得分:0)
解决了!
@Sraw是对的:它不应该导致崩溃。
解释:fetch_data()
正在使用cx Oracle Connection对象(self.db_connection
),默认情况下它不是线程安全的。将threaded
参数设置为True
会将共享连接与互斥锁包装在一起,如Cx Oracle文档中所述:
thread参数应该是一个布尔表达式 指示Oracle是否应该包含对连接的访问 用互斥量。在单线程应用程序中这样做会强制执行 性能损失约10-15%,这就是默认为False的原因。
所以我在我的代码中,我刚修改了以下内容,当用户在刷新数据时尝试访问数据时,它现在可以正常运行而不会崩溃:
# inside the connect method of the db_obj class
self.db_connection = cx.connect('connection string', threaded=True) # False by default