Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/321.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 多线程脚本在末尾挂起_Python_Multithreading_Queue - Fatal编程技术网

Python 多线程脚本在末尾挂起

Python 多线程脚本在末尾挂起,python,multithreading,queue,Python,Multithreading,Queue,你好:我正在努力让这个脚本工作。有时,根据用户数量的不同,下面的示例显示了3个用户,但很容易增加,脚本不会退出。所有作业都已完成,但脚本只是挂起而不退出。我认为问题在于我从一个真正的工人那里获得生存的方法,但我不知道有什么替代方法。有什么想法吗 import datetime, logging, os.path, queue, random, threading, time script = os.path.basename(__file__) logging.basicConfig(leve

你好:我正在努力让这个脚本工作。有时,根据用户数量的不同,下面的示例显示了3个用户,但很容易增加,脚本不会退出。所有作业都已完成,但脚本只是挂起而不退出。我认为问题在于我从一个真正的工人那里获得生存的方法,但我不知道有什么替代方法。有什么想法吗

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()

正如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
这仍然不是一个理想的实现,因为当unset队列为空但其他线程尚未完成处理时,while循环将在所有线程中快速旋转,等待最后一个线程完成


最好让线程简单地完成它们的工作并在没有剩余工作时退出,让主线程等待udone.qsize>=lenusers\u all条件变为true。

这是多线程代码的另一个版本。变化:

1线程具有专有名称thread-1,这些名称包含在日志中

2个队列包含用户实例,而不是全局数组中的索引

如果队列中没有线程,3个线程将自行停止。Init代码将许多用户放入输入队列,然后在最后为每个线程添加一个None,这样每个线程都会收到退出的信号

4工人在启动和停止时记录日志;用户对象可以直接打印

来源 输出
我的工作队列也有类似的问题。我的解决方案是,上面提到的调用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()

我希望这对您有所帮助。

您对队列的访问不同步,这将产生竞争条件。特别是,您可以基于之前对qsize的调用调用get而不使用锁。试着乐观地调用get并捕获完整或空的异常。只有当getblock=False或get_nowait时才会抛出异常。我不需要block=True吗?我对你在这里使用的逻辑有点困惑。hasDelay与DB查询相关的实际模拟是什么?从队列中取出一个工作项,开始处理它,然后改变主意把它放回去,这很奇怪。@dano我的DB脚本中有hasQuery和runNextQuery。我用Delay替换了它们,以获得一个独立的脚本。这里是原始问题:i每个用户都有查询,ii固定数量的查询可以在任何时间点在数据库上并行运行。查询必须循环运行,这意味着所有用户的查询1必须在转到查询2之前触发。即使所有其他作业都已完成,对给定用户的查询也无法并行运行…它们必须按顺序运行。在代码中,我将用户从FIFO队列中带出,并将其放回以获得循环效应。通过使用gettimeout=0.5或一些类似的小值,而不是get_nowait,可以避免在循环时过度使用CPU。我在orig post的末尾更新了worker。根据业务问题,如果线程看到队列为空,则可以退出该线程。还有足够的线程来处理其他用户,因为给定用户的作业必须按顺序运行。这克服了由于不同步队列而导致的旋转while循环问题和竞争条件。
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
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()