欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Python多线程的退出控制实现

程序员文章站 2022-07-08 09:20:43
日常前言最近接 到一个抢票的爬虫外包,那个网站及其之捞,访问购票地址竟然还要排队,在购票高峰临时升一下服务器配置不行吗…没办法,甲方爸爸的要求还得做啊,其中一个障碍便是目标网站的后端限制了访问频次,俗...

日常前言

最近接 到一个抢票的爬虫外包,那个网站及其之捞,访问购票地址竟然还要排队,在购票高峰临时升一下服务器配置不行吗…没办法,甲方爸爸的要求还得做啊,其中一个障碍便是目标网站的后端限制了访问频次,俗话说:“上有政策,下有对策。” 立刻想到了多线程 + 多代理的方式进行访问。

Python多线程的退出控制实现

但此时问题便来了,多代理还好说,再写个爬虫爬一堆下来就好,多线程可就麻烦多了,多线程一旦发出去了,基本等同于失控的状态,你无法去结束或者是重启一个线程,最多只能是获取线程的信息,没有实际的控制权,而且python官方也没有提供相应的结束函数。那么接下来,让我们来好好聊聊解决这个问题的思路。

单线程的结束

说实话,会百度在程序世界是一个优秀的习惯,不然怎么会有这么一张表情包呢

Python多线程的退出控制实现

但是百度这一次却不尽人意,搜了很久,结果不尽人意,基本上所有的搜索结果都告诉我只有结束单个线程的方法,我也试过循环使用百度的结束函数,但最终都只能是结束的当前的这一个线程,无法达到目标。

贴一段搜到的单线程结束代码示例

def _async_raise(tid, exctype):
 tid = ctypes.c_long(tid)
 if not inspect.isclass(exctype):
  exctype = type(exctype)
 res = ctypes.pythonapi.pythreadstate_setasyncexc(tid, ctypes.py_object(exctype))
 if res == 0:
  raise valueerror("invalid thread id")
 elif res != 1:
  ctypes.pythonapi.pythreadstate_setasyncexc(tid, none)
  raise systemerror("pythreadstate_setasyncexc failed")
  
def stop_thread(thread):
 _async_raise(thread.ident, systemexit)

那怎么结束多个线程呢?

既然度娘也搜不到,那就自己探索,打开,其中一个daemon属性进入了视野,单词翻译过来便是守护进程,相信大家应该或多或少的听到过,以下是官方的释义,大概意思就是只要在启动线程之前设置了这个属性为true,当父进程结束时,所有的子进程跟着全部结束,这样就好办了,接下来看看代码部分。

daemon
a boolean value indicating whether this thread is a daemon thread (true) or not (false). this must be set before start() is called, otherwise runtimeerror is raised. its initial value is inherited from the creating thread; the main thread is not a daemon thread and therefore all threads created in the main thread default to daemon = false.

完整代码

import threading,time,random

class messy:
 def __init__(self):
  self.__messy = 0

 def m(self,i):
		# 随机时间进行打印
  time.sleep(random.random()*2)
  print(i)
  if i == 1:
   self.__messy = 1

 def main(self):
  threads = []
  # 将会启动10个线程,线程id为 1 时全部线程终止!
  for i in range(10):
   t = threading.thread(target=self.m,args=(i,))
   t.daemon = 1
   threads.append(t)
  # 启动所有线程
  for i in threads:
   i.start()
  # 当标志位【 messy 】时所有多线程结束
  while 1:
   if self.__messy:
    break
  print('线程已退出!')

messy().main()
# 继续执行后续程序
for i in range(5):
 print('yeah!')

此时,main这个函数对于多线程来讲,便是父进程,也就是守护进程。预计会进行10次循环的数字打印,但是当self.__messy这个标志位为真时,所有的剩余子线程将不会再执行,直接结束进行后续的操作

e.g:如下图便只打印了四次

Python多线程的退出控制实现

最后

目前来讲,用设置主线程退出的方法是可以完成现在这个抢票的目标。

但是后来发现其实这么做也会带来很多坏处,直接杀掉所有子线程对系统来说是一个很粗鲁的行为,如果涉及到的操作包括了文件数据、数据库数据的改动的话,内存无法被合理释放(之前就遇到过cpu莫名占用满),极有可能造成数据丢失甚至系统中断

我这里只是一个抢票的小程序,子线程只用到了post,网络请求中断带来的影响还是相对来讲比较小的,所以大家酌情使用本篇所介绍的方法。

本文作者: messy
原文链接:https://www.messys.top/detail/78

到此这篇关于python多线程的退出控制实现的文章就介绍到这了,更多相关python多线程退出控制内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!