文章内容

2021/1/20 17:29:36,作 者: 黄兵

python多线程中的setDaemon

若在主线程中创建了子线程,当主线程结束时根据子线程daemon(设置thread.setDaemon(True))属性值的不同可能会发生下面的两种情况之一:

  1. 如果某个子线程的daemon属性为False,主线程结束时会检测该子线程是否结束,如果该子线程还在运行,则主线程会等待它完成后再退出;
  2. 如果某个子线程的daemon属性为True,主线程运行结束时不对这个子线程进行检查而直接退出,同时所有daemon值为True的子线程将随主线程一起结束,而不论是否运行完成。

属性daemon的值默认为False,如果需要修改,必须在调用start()方法启动线程之前进行设置。

应用场景:

当启动一个线程时设置thread.setDaemon(True),则该线程为守护线程(也可以称为后台线程)。表示该线程是不重要的,进程退出时不需要等待这个线程执行完成。这样做的意义在于:避免子线程无限死循环,导致退不出程序,也就是避免了孤儿进程的出现。

当不设置或者thread.setDaemon(False)时,主进程执行结束时,会等待线程结束。

应用:如保持网络连接(发送keep-alive心跳包)或者后台监控的线程,负责内存管理与垃圾回收(实际上JVM就是这样做的),这些线程与实际提供应用服务的线程有了逻辑上的”前/后”的概念,而如果主线程已经退出,那么这些后台线程也没有存在的必要。

如果没有这一机制,那么我们在主线程完成之后,还必须逐个地检查后台线程,然后在主线程退出之前,逐个地关闭它们. 有了前后线程的区分, 我们只需要负责管理前台线程, 完成主要的逻辑处理之后退出即可.

下面是示例代码:

def ping(self, q):
while True:
ip = q.get()
get_time_result = self.call_subprocess(ip)
if get_time_result:
get_delay_time = self.get_ping_time(get_time_result)
if get_delay_time:
print(f'{ip} 的延迟是: {get_delay_time}')
else:
print(f'{ip} 超时')
q.task_done()

@staticmethod
def get_ping_time(stdout_result):
if 'time=' in stdout_result:
get_time_start = stdout_result.index('time')
get_time_end = stdout_result.index('ms')
delay_time = stdout_result[get_time_start + 5:get_time_end]
return delay_time
else:
return False

def call_subprocess(self, host_or_ip):
""" Calls system "ping" command, returns True if ping succeeds.
Required parameter: host_or_ip (str, address of host to ping)
Optional parameters: packets (int, number of retries), timeout (int, ms to wait for response)
Does not show any output, either as popup window or in command line.
Python 3.5+, Windows and Linux compatible (Mac not tested, should work)
"""
# The ping command is the same for Windows and Linux, except for the "number of packets" flag.
if platform.system().lower() == 'windows':
command = ['ping', '-n', str(self._packets), '-w', str(self._timeout), host_or_ip]
# run parameters: capture output, discard error messages, do not show window
result = subprocess.run(command, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
creationflags=0x08000000)
# 0x0800000 is a windows-only Popen flag to specify that a new process will not create a window.
# On Python 3.7+, you can use a subprocess constant:
# result = subprocess.run(command, capture_output=True, creationflags=subprocess.CREATE_NO_WINDOW)
# On windows 7+, ping returns 0 (ok) when host is not reachable; to be sure host is responding,
# we search the text "TTL=" on the command output. If it's there, the ping really had a response.
if result.returncode == 0 and b'TTL=' in result.stdout:
return result.stdout.decode('GBK')
else:
return False
else:
command = ['ping', '-c', str(self._packets), '-w', str(self._timeout), host_or_ip]
# run parameters: discard output and error messages
result = subprocess.run(command, capture_output=True)
if result.returncode == 0:
get_time_result = result.stdout.decode('utf-8')
return get_time_result
else:
return False

def main(self):
for i in range(self._num_threads):
worker = Thread(target=self.ping, args=(self._queue,))
worker.setDaemon(True)
worker.start()
for ip in self._ips:
self._queue.put(ip)
self._queue.join()


参考资料:

1、Python多线程中的setDaemon

2、python进程,主线程, 子线程的关系

分享到:

发表评论

评论列表