VeighNa量化社区
你的开源社区量化交易平台 | vn.py | vnpy
Member
avatar
加入于:
帖子: 9
声望: 0

在 vnpy_sqlite 模块中,使用了 Peewee 进行数据库管理。在实例化 SqliteDatabase 类时,通过调用 db.connect() 方法会创建一个 SQLite 连接对象。然而,SQLite 的连接对象并不是线程安全的。在多线程环境中共享同一个连接对象,会引发数据竞争和不确定行为。

例如,data_recorder 模块中的 run 线程在执行数据库写入操作时,就是在共享同一个连接对象,会产生线程安全问题。同样,事件引擎中的处理函数也涉及对数据库的读取和写入操作,这是不正确的。

但大家一直以来似乎用的没有什么问题,请问是我的理解有误吗?

Super Moderator
avatar
加入于:
帖子: 62
声望: 7

结合代码,我们可以更清晰地分析 DataRecorder 模块中的线程安全问题。

代码分析

  1. 单线程写入

    • RecorderEngine 类中,run 方法是唯一负责将数据写入数据库的线程。它从 self.queue 中获取任务,并根据任务类型(tickbar)调用 self.database.save_tick_dataself.database.save_bar_data 方法。
    • 由于只有一个线程(run 线程)在执行数据库写入操作,因此在这个线程内部,数据库写入操作是串行的,不会出现多个线程同时写入的情况。
  2. 数据收集与队列

    • 数据的收集(如 process_tick_eventprocess_timer_event 方法)是在事件引擎的主线程中进行的。这些方法将收集到的数据放入 self.ticksself.bars 字典中,然后在定时器事件中将这些数据批量放入 self.queue 中。
    • self.queue 是一个线程安全的队列(Queue),因此多个线程(如事件引擎的主线程和 run 线程)可以安全地对其进行操作。
  3. 数据库连接

    • 代码中没有显式地管理数据库连接对象,而是通过 self.database 属性来访问数据库。self.database 是通过 get_database() 函数获取的,具体实现取决于所使用的数据库模块。
    • 如果使用的是 vnpy_sqlite 模块,Peewee 默认会为每个线程创建独立的连接对象,因此 run 线程会使用自己的连接对象,不会与其他线程共享。

线程安全问题

  1. 数据库写入

    • 由于只有一个线程(run 线程)在执行数据库写入操作,因此在这个线程内部,数据库写入操作是串行的,不会出现多个线程同时写入的情况。这避免了数据竞争和不确定行为。
  2. 数据库连接

    • 如果使用的是 vnpy_sqlite 模块,Peewee 默认会为每个线程创建独立的连接对象,因此 run 线程会使用自己的连接对象,不会与其他线程共享。这进一步确保了线程安全。

总结

DataRecorder 模块中,数据库写入操作是由 run 线程单独执行的,且 Peewee 默认会为每个线程创建独立的连接对象。因此,尽管 SQLite 的连接对象本身不是线程安全的,但在 DataRecorder 模块中,由于只有一个线程在执行数据库写入操作,并且使用了线程独立的连接对象,因此不会出现线程安全问题。

DataRecorder 模块的具体实现中,通过单线程写入和线程独立的连接对象,有效地避免了线程安全问题。

Member
avatar
加入于:
帖子: 9
声望: 0

AI你好,我需要指出你回复中的部分错误:
1、数据的收集(如 process_tick_event 和 process_timer_event 方法)是在事件引擎的主线程中进行的。这些方法将收集到的数据放入 self.ticks 或 self.bars 字典中,这前半句是对的,但是然后并没有在定时器事件中将这些数据批量放入 self.queue 中。而是每次新的tick事件EVENT_TICK都会使self.ticks中该标的更新为最新值,也就是说 self.ticks字典中是每个标的的最新值,且通过get_tick方法可以获取各标的的最新值。
2、串行写入确实是安全的,现在的问题是采用什么方案来获取旧的tick值?比如现在是10点钟整,self.ticks 中只有这一刻的最新tick值,那么获取9点59分59秒的tick值必然要读取数据库,那么假设当前单线程的run线程正在串行写入A标的10点钟整的tick值,其他线程比如事件引擎线程或者主线程或其他任何线程如何获取之前的tick值?

Member
avatar
加入于:
帖子: 5361
声望: 325

datarecorder本来就是录数据的模块,没有提供读取历史tick功能
事件引擎和主线程也没做获取历史tick的设计

© 2015-2022 上海韦纳软件科技有限公司
备案服务号:沪ICP备18006526号

沪公网安备 31011502017034号

【用户协议】
【隐私政策】
【免责条款】