所以我试图尽可能地简化问题。我知道这看起来像一面文字,但我试着给你一些背景。我创建了一个超过6百万行的简单sqlite数据库。测试表看起来像这样:
class Backlog(Base):
__tablename__ = "files"
id = Column(Integer, primary_key=True)
filename = Column(String)
date = Column(Date)
我试图看看它是否可以同时进行多线程调用,以便我获得更快的I / O.
阅读文档并检查SO后,我决定使用scoped_session
。获取代码如下所示:
from sqlalchemy import *
from sqlalchemy.orm import sessionmaker, scoped_session, Session
import queue
import threading
import time
def get_sql_proc(ThreadSession):
some_session = ThreadSession()
sql = text('select * from files')
call = some_session.execute(sql)
res = call.fetchall()
ThreadSession.remove()
return res
def add_func_to_thread(q, func, kwargs):
q.put(func(**kwargs))
engine = create_engine('sqlite:///christmas.db', echo=False)
session_factory = sessionmaker(bind=engine)
ThreadSession = scoped_session(session_factory)
q = queue.Queue()
st = time.time()
threads = list()
for i in range(1, 4):
t = threading.Thread(target=add_func_to_thread,
args=(q, get_sql_proc, {'ThreadSession': ThreadSession}))
t.daemon = True
t.start()
threads.append(t)
for x in threads:
x.join()
print(time.time()-st)
这将获得大约88秒的运行时间。但是当我一个接一个地调用它们时,我得到的运行时间约为27秒:
engine = create_engine('sqlite:///christmas.db', echo=False)
session = Session(engine)
st = time.time()
for i in range(1, 4):
w = session.execute('select * from files').fetchall()
print(time.time()-st)
我在MS SQL Server上进行了相同的测试,并得到了类似的结果。我对我做错了什么很困惑,为什么用多线程方法这么慢,任何提示或技巧都会受到赞赏
答案 0 :(得分:1)
Python有Global Interpreter Lock因此在任何给定时间实际上只会发生一次操作。 Python threading模块提供了concurrency, but not parallelism.
作为一个非常简单的类比,你可以想到在你面前有两副牌,你想看看两个牌组中的每张牌。穿线模块呈现自己好像从每个牌组中挑选一张牌并同时读取两张牌,但在幕后它只是将牌组混合在一起并一次看一张牌。
在不知道许多私密细节的情况下,我猜测性能不如预期,因为"线程"调用实际上是串行发生的,在这种情况下,线程的额外开销只会起到性能损害的作用。
Python为multiprocessing提供了一种不同的并行方法
模块。它产生(或能够或产生取决于使用)一个新的解释器进程,它并行运行所需的功能。在进程之间共享内存比使用线程更复杂,因此它可能不如将threading.Thread
更改为multiprocessing.Process
那么简单。