一、首先說下多線程、多程序用途及異同點,另外還涉及到隊列的,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秒。
2、多程序:
2.1在這裡我還想再啰嗦一句,多線程隻能利用單核CPU,也就是說不管你産生多少線程,都隻會在一個CPU上去排程,但是多程序的程序池可以實作多CPU排程,以前
的部落格裡,也提到過,如果你的任務是計算密集型的,就要使用多程序,其實就是因為多程序能排程多核cpu,是以比較快。
實作線程的子產品是multiprocessing,它裡面也有很多的方法來實作不同的目的,其實程序和線程除了子產品不同之外,它們的方法基本是都是一樣的,而且含義也是
一樣的,下面我們也是先來舉一個單線程的例子:
1 import multiprocessing
2 import time
3
4 def func(msg):
5 for i in range(3):
6 print(msg)
7 time.sleep(1)
8
9 if __name__ == "__main__":
10 p = multiprocessing.Process(target=func, args=("hello", ))
11 p.start()
12 p.join()
13 print("Sub-process done")
解釋說明:
首先先說main裡面的代碼,先建立一個程序對象,然後調用它的start方法(表明開始執行func函數),那個p.join()在這裡說一下,多線程裡面也有這個方法,
其功能和含義是相同的,表示主程序等待子程序,在我們這段代碼中,主程序就是那個print語句,等待函數執行完畢之後再執行這個print語句,下面我們來看下
輸出的結果,那麼你就恍然大明白了!!!
hello
hello
hello
Sub-process done
2.2、程序池
其實程序池就是調用的multiprocessing子產品中的Pool方法,然後完成程序池的目的,看代碼:
1 from multiprocessing import Pool
2
3 def f1(arg):
4 print(arg)
5
6 if __name__ == '__main__':
7 pool = Pool(5)
8 for i in range(5):
9 pool.apply_async(func=f1,args=(i,))
10
11 pool.close()
12 pool.join()
方式一樣,先看代碼的含義,然後再輸出結果,pool對象的apply_async方法的作用就是接受任務并把它們(任務)放到程序池中去,在這裡需要注意的是,
程序池需要關閉;然後說下那兩個5的含義,第一個5是程序池最多産生5個程序,第二個5是産生五個任務,供程序去處理; 下面看輸出的結果:
0
1
2
3
4
轉載于:https://www.cnblogs.com/madq-py/p/5692873.html