一、首先说下多线程、多进程用途及异同点,另外还涉及到队列的,memcache、redis的操作等:
1、在python中,如果一个程序是IO密集的操作,使用多线程;运算密集的操作使用多进程。
但是,其实在python中,只支持一个cpu的多线程,多个任务是切换执行的,并不能并行执行,所以有的时候,多线程并不比单线程要快,在我们的理解中,下意识的就会认为
多线程肯定比单线程要快,其实不然,多线程只会在有线程阻塞的情况下才会起到效果,下面我们来看一个实例:
1 import os,sys,json 2 import threadpool,threading 3 from collections import OrderedDict 4 import collections,time 5 import random 6 import datetime 7 8 print(datetime.datetime.now()) 9 def analysed(): 10 # count_dict = {} 11 with open(‘log.log‘) as log_file: 12 for line in log_file.readlines(): 13 line = line.strip() 14 if ‘.action?‘ in line: 15 url_api = line.split()[6].split(‘.action‘)[0] 16 if url_api.startswith(‘http‘): 17 count = count_dict.setdefault(url_api,0) 18 count += 1 19 count_dict[url_api] = count 20 else: 21 continue 22 else: 23 continue 24 25 order_dict = collections.OrderedDict(reversed(sorted(count_dict.items(),key=lambda x: x[1]))) 26 # return order_dict 27 for key in order_dict.keys(): 28 if ‘?‘ in key: 29 continue 30 else: 31 # print(key,order_dict.get(key)) 32 pass 33 print(datetime.datetime.now()) 34 count_dict = {} 35 t = threading.Thread(target=analysed,args=()) 36 t.start()
解释说明:
首先我们看下这个脚本实现的目的,主要就是打开一个日志文件,里面是nginx访问日志,提取出来部分url,然后去重排序输出重复的url数量,(在这里我没有输出,是因为方便看时间
最下面的两行就是多线程,使用的是threading的Thread的方法,里面有两个参数;一个是target,用来指定调用的函数,另一个是args,用来指定传递的参数),其它的就不过多的
解释了,直接看运行的时间:
《 2016-07-22 09:56:33.716978
2016-07-22 09:56:39.375297 》;总共用了6秒的时间,下面我们就不把所有的脚本都输出出来了,只把最后两行的内容改为analysed(count_dict),直接调用函数,然后
输出的结果为:
《 2016-07-22 10:19:57.294831
2016-07-22 10:20:02.549330 》;总共用时5秒
总结:
从输出的结果就可以看出来,使用多线程比使用单线程用时还要长,因为这个脚本执行的过程不会产生线程阻塞的问题,所以执行的时间都是差不多的,在这里多线程是起不到什么作
用的,也验证了我们上面所说的观点(线程阻塞的脚本实例就不跟大家演示了,大家只要明白这个道理就OK了)
2、说完多线程,下面我们来聊聊线程池的作用,在说线程池之前,我们先来看下队列:
队列可以有多种形式(1、先进先出,2、先进后出),其实不管是那种形式,它的作用就是把任务放在里面,然后进程或者线程去取任务来执行,下面我们来定义一个队列,并列出一些常用的
方法:
myqueue = queue.Queue() ;括号里面可以有好多的参数(其中maxsize = num)指定的是队列的最大长度
myqueue.put() ; 括号里面放置的是任务,就是把任务放到队列中
myqueue.get() ; 这个方法是获取队列中的任务,如果队列中已经没有任务了,那么就会一直处于等待状态。
myqueue.qsize() ; 这个方法是获取队列的长度
myqueue.empty() ; 如果队列为空,返回True,反之False
myqueue.full() ; 如果队列满了,返回True,反之False
附上一个例子:
1 import queue 2 import threading 3 import time 4 5 q = queue.Queue(20) 6 7 def productor(arg): 8 q.put(‘买票‘) 9 10 #定义有6个人在同时购票,但是三台服务器只能同时处理三个请求。 11 for i in range(6): 12 t = threading.Thread(target=productor,args=(i,)) 13 t.start() 14 15 def consumer(arg): 16 while True: 17 print(arg,q.get()) 18 time.sleep(2) 19 #描述为三台服务器在同时处理,一台服务器同一时刻只能处理一个请求 20 for i in range(3): 21 t = threading.Thread(target=consumer,args=(i,)) 22 t.start()
我们先看代码,然后再列出输出的结果;这段代码的意思是说,6个月在同时买票,然后又三个线程在同时处理,买票的会把买票放在队列中,也就是q.put(‘买票‘)
线程处理里面threading.Thread(target=consumer,args=(i,));括号里面分别的意思分别是:target指明线程要调用的函数,args是要传递的参数,在这
里我们只传递第几个线程去执行任务;然后consumer函数执行去队列中取任务,也就是q.get()。看完这段代码,我们再看输出的结果:
1 0 买票 2 1 买票 3 2 买票 4 0 买票 5 1 买票 6 2 买票
其实我们这样看,看的并不是很明确,其实在输出结果之后,程序还没有停止,上面我们再说队列的时候,也简单的提到了,因为,最后的q.get()会一直等待输出,
如果我们不想代码一直这么地的等待下去,那么我们可以在q.get()的括号里面加上超时时间,可以写成这种模式:q.get(timeout=5),等待5秒。