您好:我正在尝试让此脚本正常运行。有时取决于用户数量(下面的示例显示3但可以轻松增加),脚本不会退出。所有作业都已完成,但脚本只是挂起而没有退出。我认为我对while True
worker
中存在的方法是问题,但我不知道另一种选择。有什么想法吗?
import datetime, logging, os.path, queue, random, threading, time
script = os.path.basename(__file__)
logging.basicConfig(level=logging.DEBUG, format="%(asctime)-4s %(thread)6s %(message)s", datefmt="%m-%d %H:%M:%S",
filename="%s_%s.log"%(script[:script.find(".")],datetime.datetime.today().strftime("%Y%m%d-%H%M%S")))
class User(object):
def __init__(self, id, ndelay, mind, maxd):
self.id = id
self.numdelay = ndelay #number of delays
self.mind = mind #min delay
self.maxd = maxd #max delay
self.currdelaynum = 0 #index for next delay
def hasDelay(self):
if self.currdelaynum >= 0 and self.currdelaynum < self.numdelay:
return True
def runNextDelay(self):
delay = round(self.mind + random.random()*(self.maxd - self.mind))
logging.info("%s beg (delay=%d)"%(self.id,delay))
time.sleep(delay)
logging.info("%s end"%self.id)
self.currdelaynum += 1
def worker(unext,udone):
while True:
if unext.qsize() > 0:
m = unext.get()
users_all[m].runNextDelay()
if users_all[m].hasDelay():
unext.put(m)
else:
udone.put(m)
else:
if udone.qsize() >= len(users_all):
break
if __name__=='__main__':
random.seed(10)
#global users_all
users_all = list()
users_all.append(User("aa",2,3,9))
users_all.append(User("bb",3,2,4))
users_all.append(User("cc",1,4,5))
users_next = queue.Queue()
users_done = queue.Queue()
for n in range(len(users_all)):
users_next.put(n)
threads = [threading.Thread(target=worker, args=(users_next,users_done)) for n in range(2)]
for t in threads: t.start()
for t in threads: t.join()
大多数多线程python示例都有一个预先知道的作业队列。我正在编写一个脚本来测试在数据库上并行运行的查询的响应时间。为了使上面的示例自包含,我用sleep
替换了它的ODBC查询部分。我还要感谢任何有关更好实施的意见。
根据评论更新版本
def worker(unext):
while True:
try:
m = unext.get_nowait()
users_all[m].runNextDelay()
if users_all[m].hasDelay():
unext.put(m)
except queue.Empty:
break
答案 0 :(得分:0)
正如univerio评论的那样,有竞争条件。通常,当处理多个线程之间共享的对象时,请问自己这个问题,如果我的线程在此时被中断并且另一个线程被允许运行会发生什么? univerio概述的情况是qsize()
调用可能在线程A中返回非零,然后线程B运行并从同一队列中拉出一个项目。当线程A再次运行以执行get()
假设队列中的项目有误时,get()
可能会阻塞。
以下是一些未经测试的代码,可用于指导您的最终实施:
def worker(unext, udone):
while True:
try:
m = unext.get_nowait()
users_all[m].runNextDelay()
if users_all[m].hasDelay():
unext.put(m)
else:
udone.put(m)
except queue.Queue.Empty:
if udone.qsize() >= len(users_all):
break
这仍然不是一个理想的实现,因为当unext
队列为空但其他线程尚未完成处理时,while循环将在所有线程中疯狂旋转,等待最后一个线程完成。 / p>
最好让线程完成工作并在没有剩余工作时退出,让主线程等待udone.qsize() >= len(users_all)
条件变为真。
答案 1 :(得分:0)
这是多线程代码的另一个版本。变化:
1)线程有正确的名称(&#34; thread-1&#34;),它们包含在日志中
2)队列保存用户实例,而不是索引到全局数组
3)如果队列中的None为空,则线程会自行停止。初始代码put()
将多个用户放入输入队列,然后在每个线程的末尾添加一个无,这样每个线程都会发出信号退出。
import datetime, logging, os.path, random, sys, threading, time
import Queue as queue
script = os.path.basename(__file__)
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)-4s %(threadName)s %(message)s", datefmt="%m-%d %H:%M:%S",
stream=sys.stderr,
# filename="%s_%s.log"%(script[:script.find(".")],datetime.datetime.today().strftime("%Y%m%d-%H%M%S")))
)
class User(object):
def __init__(self, id, ndelay, mind, maxd):
self.id = id
self.numdelay = ndelay #number of delays
self.mind = mind #min delay
self.maxd = maxd #max delay
self.currdelaynum = 0 #index for next delay
def __repr__(self):
return '<User: id={}>'.format(self.id)
def hasDelay(self):
return (
self.currdelaynum >= 0
and self.currdelaynum < self.numdelay
)
def runNextDelay(self):
delay = round(self.mind + random.random()*(self.maxd - self.mind))
logging.info("%s beg (delay=%d)", self.id, delay)
time.sleep(delay)
logging.info("%s end", self.id)
self.currdelaynum += 1
def worker(unext, udone):
logging.info('start')
for user in iter(unext.get, None):
while True:
user.runNextDelay()
if not user.hasDelay():
break
logging.debug('%s: reloop', user)
udone.put(user)
logging.info('done')
if __name__=='__main__':
random.seed(10)
users_all = list()
users_all.append(User("aa",2,3,9))
users_all.append(User("bb",3,2,4))
users_all.append(User("cc",1,4,5))
users_next = queue.Queue()
users_done = queue.Queue()
for user in users_all:
users_next.put(user)
# flag each thread to exit at end
num_threads = 2
for _ in range(num_threads):
users_next.put(None)
threads = [
threading.Thread(
target=worker,
args=(users_next,users_done),
name='thread-{}'.format(n),
)
for n in range(num_threads)
]
for t in threads:
t.start()
for t in threads:
t.join()
08-19 12:29:29 thread-0 start
08-19 12:29:29 thread-0 aa beg (delay=6)
08-19 12:29:29 thread-1 start
08-19 12:29:29 thread-1 bb beg (delay=3)
08-19 12:29:32 thread-1 bb end
08-19 12:29:32 thread-1 <User: id=bb>: reloop
08-19 12:29:32 thread-1 bb beg (delay=3)
08-19 12:29:35 thread-0 aa end
08-19 12:29:35 thread-0 <User: id=aa>: reloop
08-19 12:29:35 thread-0 aa beg (delay=4)
08-19 12:29:35 thread-1 bb end
08-19 12:29:35 thread-1 <User: id=bb>: reloop
08-19 12:29:35 thread-1 bb beg (delay=4)
08-19 12:29:39 thread-1 bb end
08-19 12:29:39 thread-0 aa end
08-19 12:29:39 thread-0 cc beg (delay=5)
08-19 12:29:39 thread-1 done
08-19 12:29:44 thread-0 cc end
08-19 12:29:44 thread-0 done
答案 2 :(得分:0)
我的工作队列遇到了类似的问题。我的解决方案是(上面提到的)调用get()函数的超时时间为0:
def run(self):
while not self._stopevent.isSet():
try:
self._execute_job_function()
except queue.Empty:
pass #make sure the application doesn't crash when the jobqueue is empty
def _execute_job_function(self):
job = self._job_list.get(False, 0) #calling get function with time-out = 0 to prevent hanging
print("Executing job: {0}".format(job))
self._results_queue.put("{0} - Done".format(job))
self._job_list.task_done()
我希望这会对你有所帮助。