PythonSqlite3:如何使用键盘中断等快速、干净地中断长时间运行的查询

PythonSqlite3:如何使用键盘中断等快速、干净地中断长时间运行的查询,sqlite,python,Sqlite,Python,使用Python中的模块,当接收到SIGINT(例如Control-C)时,长时间运行的查询不会很快中断/取消。sqlite3提供了一个方法,但是没有关于如何使用它的示例 有没有一种简单的方法可以中断/取消通过Python/sqlite3运行的长时间查询? 为了进行说明,首先生成一个测试数据库和表: import sqlite3 from random import randint conn = sqlite3.connect("randtable.db", 10.0) cursor = co

使用Python中的模块,当接收到SIGINT(例如Control-C)时,长时间运行的查询不会很快中断/取消。sqlite3提供了一个方法,但是没有关于如何使用它的示例

有没有一种简单的方法可以中断/取消通过Python/sqlite3运行的长时间查询?

为了进行说明,首先生成一个测试数据库和表:

import sqlite3
from random import randint

conn = sqlite3.connect("randtable.db", 10.0)
cursor = conn.cursor()

cursor.execute("CREATE TABLE randint (id integer, rand integer)")

for i in range(1000000):
    if i % 1000 == 0:
        print ("{0}...".format(i))
    rand = randint(0,1000000)
    cursor.execute("INSERT INTO randint VALUES ({0},{1})".format(i,rand))

conn.commit()
conn.close()
然后在终端中执行长时间运行的Python/sqlite3脚本,并尝试使用Control-C中断该脚本:

from __future__ import print_function
import sqlite3

def main():
    # Long running query (pathological by design)
    statement ='''
SELECT DISTINCT a.id,a.rand
FROM randint a
     JOIN randint b ON a.id=b.rand
     JOIN randint c ON a.id=c.rand
     JOIN randint d ON a.id=d.rand
     JOIN randint e ON a.id=e.rand
     JOIN randint f ON a.id=f.rand
     JOIN randint g ON a.id=g.rand
     JOIN randint h ON a.id=h.rand
ORDER BY a.id limit 10'''

    conn = sqlite3.connect('randtable.sqlite', 10.0)
    cursor = conn.cursor()

    print ("Executing query")

    cursor.execute(statement)
    rows = cursor.fetchall()

    print ("ROWS:")
    for row in rows:
        print ("  ", row)

    conn.close()

    return

if __name__ == "__main__":
    main()
在终端中运行上述脚本,然后按Control-C(或以其他方式发送SIGINT)将最终取消查询和脚本,但这可能需要相当长的时间,也可能需要很多分钟。当按下Control-C时,在中运行的完全相同的查询几乎立即被取消


提前谢谢

回答我自己的问题,因为我想我已经解决了。下面是我想到的,对这段代码的任何评论都将不胜感激

#!/usr/bin/env python

from __future__ import print_function
import sqlite3
import threading
import signal
import os
import time

conn = None
shutdown = False

def main():
    global conn

    # Long running query (pathological by design)
    statement ='''
SELECT DISTINCT a.id,a.rand
FROM randint a
     JOIN randint b ON a.id=b.rand
     JOIN randint c ON a.id=c.rand
     JOIN randint d ON a.id=d.rand
     JOIN randint e ON a.id=e.rand
     JOIN randint f ON a.id=f.rand
     JOIN randint g ON a.id=g.rand
     JOIN randint h ON a.id=h.rand
ORDER BY a.id limit 10'''

    conn = sqlite3.connect('randtable.sqlite', 10.0)
    cursor = conn.cursor()

    print ("Executing query")

    try:
        cursor.execute(statement)
    except Exception as err:
        if str(err) != "interrupted":
            print ("Database error: {0}".format(str(err)))
        return None

    rows = cursor.fetchall()

    print ("ROWS:")
    for row in rows:
        print ("  ", row)

    conn.close()
    conn = None

    return

def interrupt(signum, frame):
    global conn
    global shutdown

    print ("Interrupt requested")

    if conn:
        conn.interrupt()

if __name__ == "__main__":
    signal.signal(signal.SIGINT, interrupt)

    mainthread = threading.Thread(target=main)
    mainthread.start()

    while mainthread.isAlive():
        time.sleep(0.2)

你的答案涵盖了这一点,但是(昨天我忘记了——对不起!)我记得我答应过要写一个答案,所以这里有另一个版本证明你可以不用全局变量来完成这一点。我在这里还使用了
线程化.Event
而不是信号来演示有几种不同的方法可以向线程发出信号,表示是时候做点什么了(但出于您的目的,请坚持使用信号,因为这非常适合对Ctrl+C作出反应):


那么呢?@DaveJones是的,正如问题最上面提到的,这应该是解决问题的方法。但是如何使用真正的代码呢?我尝试过但没有成功,也许我在Python方面的专业知识不足让我看不到显而易见的东西。对不起,错过了这一点-我现在必须去接我的女儿,但我会在一点时间内写一个例子(它将类似于
threading.Thread(target=conn.interrupt).start()
,如果这足以让您同时开始学习的话)。
import sqlite3
import time
import threading

# Background thread that'll kill our long running query after 1 second
def kill_it(connection, event):
    event.wait()
    time.sleep(1)
    connection.interrupt()

# Make some tables with lots of data so we can make a long running query
def big_query(conn, kill_event):
    print('Making big tables')
    conn.execute(
        "CREATE TABLE foo (i integer primary key, s text);")
    conn.execute(
        "CREATE TABLE bar (j integer primary key, s text);")
    conn.execute(
        "INSERT INTO foo VALUES %s" % ", ".join("(%d, 'foo')" % i for i in range(10000)))
    conn.execute(
        "INSERT INTO bar VALUES %s" % ", ".join("(%d, 'bar')" % i for i in range(10000)))
    kill_event.set()
    print('Running query')
    cur = conn.cursor()
    cur.execute(
        "SELECT * FROM foo, bar")
    print(len(cur.fetchall()))

def main():
    conn = sqlite3.connect('foo.db')
    kill_event = threading.Event()
    kill_thread = threading.Thread(target=kill_it, args=(conn, kill_event))
    kill_thread.start()
    big_query(conn, kill_event)
    kill_thread.join()

if __name__ == '__main__':
    main()