在 Django 应用中实现一个简单的网络任务时,发现只有第一次运行时代码能够正常工作,之后的运行会返回以下错误:
Traceback:
File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py" in get_response
111. response = callback(request, *callback_args, **callback_kwargs)
File "/var/www/propingui/ping/views.py" in ping
32. p = Pool(len(fls))
File "/usr/lib/python2.7/multiprocessing/pool.py" in __init__
674. Pool.__init__(self, processes, initializer, initargs)
File "/usr/lib/python2.7/multiprocessing/pool.py" in __init__
134. self._repopulate_pool()
File "/usr/lib/python2.7/multiprocessing/pool.py" in _repopulate_pool
197. w.start()
File "/usr/lib/python2.7/multiprocessing/dummy/__init__.py" in start
73. self._parent._children[self] = None
Exception Type: AttributeError at /
Exception Value: '_DummyThread' object has no attribute '_children'
经过调查发现,这个错误与在 Django 中使用 ThreadPool 有关。
2、解决方案: 为了解决这个问题,有以下几种解决方案:
-
使用 threading 模块:
- 将 ThreadPool 替换为 threading 模块,使用 threading 模块中的 Thread 类来创建线程。这种方法可以在 Django 中正常运行,但性能可能不如 ThreadPool。
- 代码示例:
import threading def _ping((host, firing_location)): pinger = Pyro4.Proxy("PYRONAME:" + firing_location) return pinger.ping(host) def ping(request): if request.method == 'POST': form = PingForm(request.POST) if form.is_valid(): host = form.cleaned_data['host'] fls = ['g1','a1'] jobs = [] for fl in fls: t = threading.Thread(target=_ping, args=((host, fl),)) t.start() jobs.append(t) ... return ... -
使用 patch 修复:
- 因为这个错误是 Python 的一个已知问题,所以可以应用 patch 来修复它。
- 代码示例:
import multiprocessing.dummy as multiprocessing if hasattr(multiprocessing.current_thread(), "_children"): multiprocessing.current_thread()._children = weakref.WeakKeyDictionary() -
在 Pyro 服务中分发 ping 任务:
- 另一种解决方法是创建一个 Pyro 服务,该服务将 ping 任务分发到所有服务器。这种方法可以避免在 Django 中使用线程或 fork,从而绕过这个问题。
- 代码示例:
import Pyro4 @Pyro4.expose class PingService: def ping(self, host): # Perform the ping operation here daemon = Pyro4.Daemon() uri = daemon.register(PingService) # Start the Pyro daemon daemon.requestLoop() -
使用自定义 ThreadPool:
- 也可以实现自定义 ThreadPool 或复用其他人的实现,例如 ThreadPoolExecutor 或 multiprocessing.pool.ThreadPool。
- 代码示例:
from concurrent.futures import ThreadPoolExecutor def _ping((host, firing_location)): pinger = Pyro4.Proxy("PYRONAME:" + firing_location) return pinger.ping(host) def ping(request): if request.method == 'POST': form = PingForm(request.POST) if form.is_valid(): host = form.cleaned_data['host'] fls = ['g1','a1'] with ThreadPoolExecutor(max_workers=len(fls)) as executor: jobs = executor.map(_ping, zip([host]*len(fls), fls)) ... return ...