python 多线程、多进程编写

写在前面

程序效率有点低,搞了下多线程提高下效率,记录在这里。

多线程

有很多编写方式,首先推荐几个包:

  • threading
  • concurrent

我实用的是concurrent这个包,因为它里面有现成的线程池,只需设置并发执行的线程数即可,代码:

    thread_list = []
    if args.domainfile:
        executor = ThreadPoolExecutor(max_workers=1000)
        domain_list = process_file(args.domainfile)
        # lock = threading.Lock()
        for domain in domain_list:
            thread_list.append(executor.submit(scrape_and_verify_scts,
                                               domain, args.port,
                                               args.verification_tasks,
                                               ctlogs, basedir))
        executor.shutdown(wait=True)
    wait(thread_list, return_when=ALL_COMPLETED)

首先要说的是,每个线程里面的局部变量是互相不影响的(查阅了很多资料得出,但不是很确定)。所以如果有全局变量则需要上锁。总结下我遇到的几种上锁的情况:

  • 打印输出数据混乱,因为并发执行的原因,所以打印需要上锁。
  • 输出到文件混乱,同上。

上锁之后效率会受影响,所以我采用将每个线程写入到一个文件,后面再将文件整合的方式,目前来看整合文件也有点慢。

说一下代码中的意思:

  • submit:提交一个线程到线程池
  • ThreadPoolExecutor:创建线程池
  • shutdown:等所有的线程结束,释放资源
  • wait:主线程阻塞,等待所有线程执行完毕

包的导入: from concurrent.futures import ThreadPoolExecutor, wait, ALL_COMPLETED, FIRST_COMPLETED


再说一下Thread重写类:

# 做一些改变,目前程序效率极低,希望采用多进程来解决这个问题,目前想法是将每个domain name看作一个进程,并行来处理。
# class ConnThread(threading.Thread):
#     def __init__(self, func, domain, port, verification_tasks, logs, lock):
#         threading.Thread.__init__(self)
#         # super(ConnThread, self).__init__()
#         self.func = func
#         self.domain = domain
#         self.port = port
#         self.verification_tasks = verification_tasks
#         self.logs = logs
#         self.lock = lock
#
#     def run(self):
#         self.func(self.domain, self.port, self.verification_tasks, self.logs, self.lock)

将thread类重写,run里面就是你要开启的线程函数。

多进程

包:multiprocessing

用法基本和多线程一致,concurrent里面也有对应多进程的进程池。

总结

简单总结下:python中的多线程适合I/O交互比较多的程序,而多进程适合计算任务比较繁重的程序。

原因:python的GIL锁,导致多线程只能运行在一个核上,如果电脑多核那么很浪费资源。所以计算任务偏向多进程,多进程可以使用多个核进行工作,cpu可以疯狂计算。但是进程不能开太多,受计算机硬件限制,所以I/O操作更偏向于多线程。

后续

出现了新问题:程序运行一段时间后会被killed掉,原因是内存爆了,linux的保护机制将其kill。所以我们还需要考虑内存占用的问题,解决方法见:

思路:我们现在有一个100万的域名列表,然后将其每个域名看作一个线程,这样就使得其一直submit。然后存在缓存中,最终内存爆掉。所以需要控制submit的数量。比如提交1000个然后就break,等到少于1000个再往里提交,通过这样的方式,就可以控制缓存的占用。

还会出现提交的数量不够用的情况,这时就需要根据你设置的线程池的大小来确定该数量的大小。
我的例子:线程池:1000,提交:2000。明显感觉到速度不如以前,所以还得增加。(不是这个原因,我程序有bug,按照别人的那个方法完全可行)
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值