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)
那怎么结束多个线程呢?

既然度娘也搜不到,那就自己探索,打开python threading模块的官方文档,其中一个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:如下图便只打印了四次
在这里插入图片描述

最后

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

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

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

本文作者: Messy
原文链接:https://www.messys.top/detail/78
版权声明: 本博客所有文章除特别声明外, 均采用 CC BY-NC-SA 4.0 许可协议. 转载请注明出处!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值