我正在研究一个多线程Python脚本,该脚本获取文件名列表并将它们放在队列中。大部分时间它都有效,但我偶尔会发现它卡住了,'ps -efL'会显示两个线程为python脚本打开。我用strace跟着它,并且返回了6个线程中的5个,但是一个只是在futex中等待永远等待。
以下是相关代码块。
threads = 6
for fileName in fileNames:
queue.put(fileName)
for i in range(threads):
t = threading.Thread(target=get_backup_list, args=(queue,dbCreds,arguments.verbose,arguments.vault))
activeThreads.append(t)
t.start()
for activeThread in activeThreads:
activeThread.join()
def get_backup_list(queue,dbCreds,verbosity,vault):
backupFiles = []
while True:
if queue.empty() == True:
return
fileName = queue.get()
try:
fileInfo = lookup_file_by_path(fileName,dbCreds,vault)
if not fileInfo:
start = time.time()
attributes = get_attributes(fileName,verbosity)
end = time.time() - start
if verbosity: print("finished in ") + str(end) + (" seconds")
insert_file(attributes,dbCreds,vault)
fileInfo = lookup_file_by_path(fileName,dbCreds,vault)
except Exception, e:
print("error on " + fileName + " " + str(e))
return
def lookup_file_by_path(path,dbCreds,vault):
attributes = {}
conn = mdb.connect(dbCreds['server'] , dbCreds['user'], dbCreds['password'], dbCreds['database'], cursorclass=MySQLdb.cursors.DictCursor);
c = conn.cursor()
c.execute('''SELECT * FROM {} where path = "%s" '''.format(vault) % ( path ) )
data = c.fetchone()
if data:
for key in data.keys():
attributes[key] = data[key]
conn.close
return attributes
我在这里做了一些根本错误的事情导致了竞争条件吗?或者还有其他我想念的东西。
谢谢, 托马斯C
答案 0 :(得分:3)
您的代码中存在争用条件:
while True:
if queue.empty() == True:
return
fileName = queue.get()
首先,线程检查队列是否为空。如果不是,则尝试阻止get
。但是,在调用queue.empty()
和queue.get
之间的时间内,另一个线程可能已经从队列中消耗了最终项,这意味着get
调用将永远阻塞。你应该这样做:
try:
fileName = queue.get_nowait()
except Queue.Empty:
return
如果这不能解决问题,你可以在线程方法中抛出一些print
语句来确定它被卡住的确切位置,并从那里开始。但是,没有其他并发问题向我发出。
修改强>
顺便说一句,你在这里所做的事情可以更加干净地实施为ThreadPool
或multiprocessing.Pool
:
from multiprocessing.pool import ThreadPool
from functools import partial
def get_backup_list(dbCreds, verbosity, vault, fileName):
backupFiles = []
fileInfo = lookup_file_by_path(fileName,dbCreds,vault)
...
if __name__ == "__main__":
pool = ThreadPool(6) # You could use a multiprocessing.Pool, too
func = partial(get_backup_list, dbCreds, arguments.verbose, arguments.vault)
pool.map(func, fileNames)
pool.close()
pool.join()
根据每个get_backup_list
调用的工作量,您可能会发现它作为multiprocessing.Pool
表现更好,因为它可以绕过全局解释器锁(GIL),这可以防止Python线程同时跨CPU核心执行。看起来你的代码可能是I / O绑定的,所以ThreadPool
可能会很好。