天天看點

python 多線程、多程序

一、首先說下多線程、多程序用途及異同點,另外還涉及到隊列的,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