• 欢迎访问挑战自我博客网站,安全研究,web渗透,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站,欢迎加入挑战自我博客网站 网站主页

python的多线程模式

python 挑战自我 961次浏览 已收录 0个评论

1、进程与线程的历史

我们都知道计算机是由硬件和软件组成的。硬件中的CPU是计算机的核心,它承担计算机的所有任务。 操作系统是运行在硬件之上的软件,是计算机的管理者,它负责资源的管理和分配、任务的调度。
程序是运行在系统上的具有某种功能的软件,比如说浏览器,音乐播放器等。 每次执行程序的时候,都会完成一定的功能,比如说浏览器帮我们打开网页,为了保证其独立性,就需要一个专门的管理和控制执行程序的数据结构——进程控制块。
进程就是一个程序在一个数据集上的一次动态执行过程。 进程一般由程序、数据集、进程控制块三部分组成。我们编写的程序用来描述进程要完成哪些功能以及如何完成;数据集则是程序在执行过程中所需要使用的资源;进程控制块用来记录进程的外部特征,描述进程的执行变化过程,系统可以利用它来控制和管理进程,它是系统感知进程存在的唯一标志。

在早期的操作系统里,计算机只有一个核心,进程执行程序的最小单位,任务调度采用时间片轮转的抢占式方式进行进程调度。每个进程都有各自的一块独立的内存,保证进程彼此间的内存地址空间的隔离。
随着计算机技术的发展,进程出现了很多弊端,一是进程的创建、撤销和切换的开销比较大,二是由于对称多处理机(对称多处理机(SymmetricalMulti-Processing)又叫SMP,是指在一个计算机上汇集了一组处理器(多CPU),各CPU之间共享内存子系统以及总线结构)的出现,可以满足多个运行单位,而多进程并行开销过大

这个时候就引入了线程的概念。 线程也叫轻量级进程,它是一个基本的CPU执行单元,也是程序执行过程中的最小单元,由线程ID、程序计数器、寄存器集合 和堆栈共同组成。线程的引入减小了程序并发执行时的开销,提高了操作系统的并发性能。 线程没有自己的系统资源,只拥有在运行时必不可少的资源。但线程可以与同属与同一进程的其他线程共享进程所拥有的其他资源。

在了解了进程和线程后,我们再来看Python中的并发实现,不用想,Python中一定有这种类似模块,没错,这个模块就是threading, 不过学习Python的人都知道,在python中有这么一个玩意儿,叫GIL,中文名叫全局解释器锁,这个锁能保证同一个时刻只有一个线程在运行,这个就保证了在python内部是线程安全的,解决了线程间数据一致性和状态同步的困难,但问题来了,这问题就是你即使编写的多线程代码,运行时其实还是在单线程执行,没法实现真正的多线程,这里说的情况是CPU密集型的情况,如果是IO密集型的情况下,是允许其它线程在这个线程等待I/O的时候运行的,所以结论是,Python的多线程在多核CPU上,IO密集型的程序能更适合利用多线程

2、常规代码完成运维工作

下面我们从一个实际的运维工作例子中来,比如你们公司有100个站点需要维护,你需要定时检测这些站点是否可以正常访问,我们以此需求背景来完成这个代码例子,首先我们用常规方法编写代码,然后在用threading模块实现并发,然后对比看效果,代码如下:

#!/usr/bin/evn python

import requests
import time

def get_site_code(url):
    r = requests.get(url)
    status = r.status_code
    line = url +  ' ' + str(status)
    with open('/tmp/site_stauts.txt', 'a+') as f:
        f.writelines(line + '\n')
        
if __name__ == '__main__':
    print 'starting at:', time.ctime()
    for url in open('urls.txt'):
        url = url.strip()
        get_site_code(url)
    print 'Done at:', time.ctime()

判断一个站点是否正常,最常用的方法就是获得这个站点的http状态码,在这里我简化了需求,只把获得的状态码写入到了文件中, 如果要做监控可以读取这个文件,如果不是2xx或3xx的,就可以报警了,我们把要检查的站点写入urls.txt文件中,通过for循环,调用get_site_code()函数将获得的站点状态码写入site_stauts.txt文件中,加入time模块主要就是对比先后运行时间,运行结果:

starting at: Sun Oct 22 19:32:23 2017
Done     at: Sun Oct 22 19:32:40 2017

3、并发模式完成常规工作

执行完成一共花了17秒时间,接下来我们采用并发方式修改下这个执行代码,如下:

#!/usr/bin/evn python

import requests
import time
import threading

def get_site_code(url):
    r = requests.get(url)
    status = r.status_code
    line = url +  ' ' + str(status)
    with open('/tmp/site_stauts.txt', 'a+') as f:
        f.writelines(line + '\n')

if __name__ == '__main__':
    print 'starting at:', time.ctime()
    threads = []
    for url in open('urls.txt'):
        url = url.strip()
        t = threading.Thread(target=get_site_code, args=(url,))
        threads.append(t)

    #print len(threads)

    for i in range(len(threads)):
        threads[i].start()

    for i in range(len(threads)):
        threads[i].join()

    print 'Done at:', time.ctime()

运行结果如下:

starting at: Sun Oct 22 19:36:49 2017
Done     at: Sun Oct 22 19:36:51 2017

我们看只用了2秒,快了8倍,看完结果接下来我们说下代码,在这例子里我选择了我个人认为最简单的方法,就是在实例化每个Thread对象的时候传入了我们定义的函数get_site_code()和需要的参数url, 实例化后得到一个Thread的实例t,我们把这个t加入线程列表threads中,接下来循环这个列表开始调用start()函数去执行,除了start()函数,我们还用到了join()函数,这个函数允许主线程等待线程结束。


挑战自我博客, 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明python的多线程模式
喜欢 (10)
支付宝[]
分享 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址