Python三大“程”!線程,進程和協程詳解!這還學不會?不存在的

What is a Thread?

Python三大“程”!線程,進程和協程詳解!這還學不會?不存在的

What is a Process?

Python三大“程”!線程,進程和協程詳解!這還學不會?不存在的

進程與線程的區別?

  1. 線程是執行的指令集,進程是資源的集合
  2. 線程的啟動速度要比進程的啟動速度要快
  3. 兩個線程的執行速度是一樣的
  4. 進程與線程的運行速度是沒有可比性的
  5. 線程共享創建它的進程的內存空間,進程的內存是獨立的。
  6. 兩個線程共享的數據都是同一份數據,兩個子進程的數據不是共享的,而且數據是獨立的;
  7. 同一個進程的線程之間可以直接交流,同一個主進程的多個子進程之間是不可以進行交流,如果兩個進程之間需要通信,就必須要通過一箇中間代理來實現;
  8. 一個新的線程很容易被創建,一個新的進程創建需要對父進程進行一次克隆
  9. 一個線程可以控制和操作同一個進程裡的其他線程,線程與線程之間沒有隸屬關係,但是進程只能操作子進程
  10. 改變主線程,有可能會影響到其他線程的行為,但是對於父進程的修改是不會影響子進程;

一個多併發的小腳本

  1. import threading
  2. import time
  3. def Princ(String):
  4. print('task', String)
  5. time.sleep(5)
  6. # target=目標函數, args=傳入的參數
  7. t1 = threading.Thread(target=Princ, args=('t1',))
  8. t1.start()
  9. t2 = threading.Thread(target=Princ, args=('t1',))
  10. t2.start()
  11. t3 = threading.Thread(target=Princ, args=('t1',))
  12. t3.start()

參考文檔

進程與線程的一個簡單解釋

http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html

Linux進程與線程的區別

https://my.oschina.net/cnyinlinux/blog/422207

Python三大“程”!線程,進程和協程詳解!這還學不會?不存在的

Python三大“程”!線程,進程和協程詳解!這還學不會?不存在的

多線程

多線程在Python內實則就是一個假象,為什麼這麼說呢,因為CPU的處理速度是很快的,所以我們看起來以一個線程在執行多個任務,每個任務的執行速度是非常之快的,利用上下文切換來快速的切換任務,以至於我們根本感覺不到。

但是頻繁的使用上下文切換也是要耗費一定的資源,因為單線程在每次切換任務的時候需要保存當前任務的上下文。

什麼時候用到多線程?

首先IO操作是不佔用CPU的,只有計算的時候才會佔用CPU(譬如1+1=2),Python中的多線程不適合CPU密集型的任務,適合IO密集型的任務(sockt server)。

啟動多個線程

主進程在啟動之後會啟動一個主線程,下面的腳本中讓主線程啟動了多個子線程,然而啟動的子線程是獨立的,所以主線程不會等待子線程執行完畢,而是主線程繼續往下執行,並行執行。

  1. for i in range(50):
  2. t = threading.Thread(target=Princ, args=('t-%s' % (i),))
  3. t.start()

join()

join()方法可以讓程序等待每一個線程之後完成之後再往下執行,又成為串行執行。

  1. import threading
  2. import time
  3. def Princ(String):
  4. print('task', String)
  5. time.sleep(1)
  6. for i in range(50):
  7. t = threading.Thread(target=Princ, args=('t-%s' % (i),))
  8. t.start()
  9. # 當前線程執行完畢之後在執行後面的線程
  10. t.join()

讓主線程阻塞,子現在並行執行

  1. import threading
  2. import time
  3. def Princ(String):
  4. print('task', String)
  5. time.sleep(2)
  6. # 執行子線程的時間
  7. start_time = time.time()
  8. # 存放線程的實例
  9. t_objs = []
  10. for i in range(50):
  11. t = threading.Thread(target=Princ, args=('t-%s' % (i),))
  12. t.start()
  13. # 為了不讓後面的子線程阻塞,把當前的子線程放入到一個列表中
  14. t_objs.append(t)
  15. # 循環所有子線程實例,等待所有子線程執行完畢
  16. for t in t_objs:
  17. t.join()
  18. # 當前時間減去開始時間就等於執行的過程中需要的時間
  19. print(time.time() - start_time)

查看主線程與子線程

  1. import threading
  2. class MyThreading(threading.Thread):
  3. def __init__(self):
  4. super(MyThreading, self).__init__()
  5. def run(self):
  6. print('我是子線程: ', threading.current_thread())
  7. t = MyThreading()
  8. t.start()
  9. print('我是主線程: ', threading.current_thread())

輸出如下:

  1. C:\Python\Python35\python.exe E:/MyCodeProjects/進程與線程/s3.py
  2. 我是子線程:
  3. 我是主線程: <_mainthread started=""/>

  4. Process finished with exit code 0

查看當前進程的活動線程個數

  1. import threading
  2. class MyThreading(threading.Thread):
  3. def __init__(self):
  4. super(MyThreading, self).__init__()
  5. def run(self):
  6. print('www.anshengme.com')
  7. t = MyThreading()
  8. t.start()
  9. print('線程個數: ', threading.active_count())

輸出如下:

  1. C:\Python\Python35\python.exe E:/MyCodeProjects/進程與線程/s3.py
  2. www.anshengme.com
  3. # 一個主線程和一個子線程
  4. 線程個數: 2

  5. Process finished with exit code 0

Event

Event是線程間通信最間的機制之一:一個線程發送一個event信號,其他的線程則等待這個信號。用於主線程控制其他線程的執行。 Events 管理一個flag,這個flag可以使用set

()設置成True或者使用clear()重置為False,wait()則用於阻塞,在flag為True之前。flag默認為False。

選項描述Event.wait([timeout])堵塞線程,直到Event對象內部標識位被設為True或超時(如果提供了參數timeout)Event.set()將標識位設為TureEvent.clear()將標識伴設為FalseEvent.isSet()判斷標識位是否為Ture

  1. #!/use/bin/env python
  2. # _*_ coding: utf-8- _*_

  3. import threading

  4. def runthreading(event):
  5. print("Start...")
  6. event.wait()
  7. print("End...")
  8. event_obj = threading.Event()
  9. for n in range(10):
  10. t = threading.Thread(target=runthreading, args=(event_obj,))
  11. t.start()

  12. event_obj.clear()
  13. inp = input("True/False?>> ")
  14. if inp == "True":
  15. event_obj.set()
  16. `

守護進程(守護線程)

一個主進程可以啟動多個守護進程,但是主進程必須要一直運行,如果主進程掛掉了,那麼守護進程也會隨之掛掉

程序會等待主線程(進程)執行完畢,但是不會等待守護進程(線程)

  1. import threading
  2. import time

  3. def Princ(String):
  4. print('task', String)
  5. time.sleep(2)
  6. for i in range(50):
  7. t = threading.Thread(target=Princ, args=('t-%s' % (i),))
  8. t.setDaemon(True) # 把當前線程設置為守護線程,要在start之前設置
  9. t.start()

場景預設: 比如現在有一個FTP服務,每一個用戶連接上去的時候都會創建一個守護線程,現在已經有300個用戶連接上去了,就是說已經創建了300個守護線程,但是突然之間FTP服務宕掉了,這個時候就不會等待守護線程執行完畢再退出,而是直接退出,如果是普通的線程,那麼就會登臺線程執行完畢再退出。

  1. #!/use/bin/env python
  2. # _*_ coding:utf-8 _*_

  3. from multiprocessing import Process
  4. import time
  5. def runprocess(arg):
  6. print(arg)
  7. time.sleep(2)


  8. p = Process(target=runprocess, args=(11,))
  9. p.daemon=True
  10. p.start()

  11. print("end")

線程之間的數據交互與鎖(互斥鎖)

python2.x需要加鎖,但是在python3.x上面就不需要了

  1. # _*_ coding:utf-8 _*_
  2. import threading
  3. def Princ():
  4. # 獲取鎖
  5. lock.acquire()
  6. # 在函數內可以直接修改全局變量
  7. global number
  8. number += 1
  9. # 為了避免讓程序出現串行,不能加sleep
  10. # time.sleep(1)
  11. # 釋放鎖
  12. lock.release()
  13. # 鎖
  14. lock = threading.Lock()
  15. # 主線程的number
  16. number = 0
  17. t_objs = []
  18. for i in range(100):
  19. t = threading.Thread(target=Princ)
  20. t.start()
  21. t_objs.append(t)
  22. for t in t_objs:
  23. t.join()
  24. print('Number:', number)

遞歸鎖(Lock/RLock)

  1. import threading
  2. def run1():
  3. print("grab the first part data")
  4. lock.acquire()
  5. global num
  6. num += 1
  7. lock.release()
  8. return num
  9. def run2():
  10. print("grab the second part data")
  11. lock.acquire()
  12. global num2
  13. num2 += 1
  14. lock.release()
  15. return num2
  16. def run3():
  17. lock.acquire()
  18. res = run1()
  19. print('--------between run1 and run2-----')
  20. res2 = run2()
  21. lock.release()
  22. print(res, res2)
  23. t_objs = []
  24. if __name__ == '__main__':
  25. num, num2 = 0, 0
  26. lock = threading.RLock() # RLock()類似創建了一個字典,每次退出的時候找到字典的值進行退出
  27. # lock = threading.Lock() # Lock()會阻塞在這兒
  28. for i in range(10):
  29. t = threading.Thread(target=run3)
  30. t.start()
  31. t_objs.append(t)
  32. for t in t_objs:
  33. t.join()
  34. print(num, num2)

信號量(Semaphore)

互斥鎖同時只允許一個線程更改數據,而Semaphore是同時允許一定數量的線程更改數據

  1. import threading
  2. import time
  3. def run(n):
  4. semaphore.acquire() # 獲取信號,信號可以有多把鎖
  5. time.sleep(1) # 等待一秒鐘
  6. print("run the thread: %s\n" % n)
  7. semaphore.release() # 釋放信號
  8. t_objs = []
  9. if __name__ == '__main__':
  10. semaphore = threading.BoundedSemaphore(5) # 聲明一個信號量,最多允許5個線程同時運行
  11. for i in range(20): # 運行20個線程
  12. t = threading.Thread(target=run, args=(i,)) # 創建線程
  13. t.start() # 啟動線程
  14. t_objs.append(t)
  15. for t in t_objs:
  16. t.join()
  17. print('>>>>>>>>>>>>>')

以上代碼中,類似與創建了一個隊列,最多放5個任務,每執行完成一個任務就會往後面增加一個任務。

多進程

多進程的資源是獨立的,不可以互相訪問。

啟動一個進程

  1. from multiprocessing import Process
  2. import time
  3. def f(name):
  4. time.sleep(2)
  5. print('hello', name)
  6. if __name__ == '__main__':
  7. # 創建一個進程
  8. p = Process(target=f, args=('bob',))
  9. # 啟動
  10. p.start()
  11. # 等待進程執行完畢
  12. p.join(

在進程內啟動一個線程

  1. from multiprocessing import Process
  2. import threading
  3. def Thread(String):
  4. print(String)
  5. def Proces(String):
  6. print('hello', String)
  7. t = threading.Thread(target=Thread, args=('Thread %s' % (String),)) # 創建一個線程
  8. t.start() # 啟動它
  9. if __name__ == '__main__':
  10. p = Process(target=Proces, args=('World',)) # 創建一個進程
  11. p.start() # 啟動
  12. p.join() # 等待進程執行完畢

啟動一個多進程

  1. from multiprocessing import Process
  2. import time
  3. def f(name):
  4. time.sleep(2)
  5. print('hello', name)
  6. if __name__ == '__main__':
  7. for n in range(10): # 創建一個進程
  8. p = Process(target=f, args=('bob %s' % (n),))
  9. # 啟動
  10. p.start()
  11. # 等待進程執行完畢

獲取啟動進程的PID

  1. # _*_ coding:utf-8 _*_
  2. from multiprocessing import Process
  3. import os
  4. def info(String):
  5. print(String)
  6. print('module name:', __name__)
  7. print('父進程的PID:', os.getppid())
  8. print('子進程的PID:', os.getpid())
  9. print("\n")
  10. def ChildProcess():
  11. info('\033[31;1mChildProcess\033[0m')
  12. if __name__ == '__main__':
  13. info('\033[32;1mTheParentProcess\033[0m')
  14. p = Process(target=ChildProcess)
  15. p.start()

輸出結果

  1. C:\Python\Python35\python.exe E:/MyCodeProjects/多進程/s1.py
  2. TheParentProcess
  3. module name: __main__
  4. # Pycharm的PID
  5. 父進程的PID: 6888
  6. # 啟動的腳本PID
  7. 子進程的PID: 4660

  8. ChildProcess
  9. module name: __mp_main__
  10. # 腳本的PID
  11. 父進程的PID: 4660
  12. # 父進程啟動的子進程PID
  13. 子進程的PID: 8452

  14. Process finished with exit code 0

進程間通信

默認情況下進程與進程之間是不可以互相通信的,若要實現互相通信則需要一箇中間件,另個進程之間通過中間件來實現通信,下面是進程間通信的幾種方式。

進程Queue

  1. # _*_ coding:utf-8 _*_
  2. from multiprocessing import Process, Queue
  3. def ChildProcess(Q):
  4. Q.put(['Hello', None, 'World']) # 在Queue裡面上傳一個列表
  5. if __name__ == '__main__':
  6. q = Queue() # 創建一個Queue
  7. p = Process(target=ChildProcess, args=(q,)) # 創建一個子進程,並把Queue傳給子進程,相當於克隆了一份Queue
  8. p.start() # 啟動子進程
  9. print(q.get()) # 獲取q中的數據
  10. p.join()

管道(Pipes)

  1. # _*_ coding:utf-8 _*_
  2. from multiprocessing import Process, Pipe
  3. def ChildProcess(conn):
  4. conn.send(['Hello', None, 'World']) # 寫一段數據
  5. conn.close() # 關閉
  6. if __name__ == '__main__':
  7. parent_conn, child_conn = Pipe() # 生成一個管道實例,parent_conn, child_conn管道的兩頭
  8. p = Process(target=ChildProcess, args=(child_conn,))
  9. p.start()
  10. print(parent_conn.recv()) # 收取消息
  11. p.join()

數據共享(Managers)

  1. # _*_ coding:utf-8 _*_
  2. # _*_ coding:utf-8 _*_
  3. from multiprocessing import Process, Manager
  4. import os

  5. def ChildProcess(Dict, List):
  6. Dict['k1'] = 'v1'
  7. Dict['k2'] = 'v2'
  8. List.append(os.getpid()) # 獲取子進程的PID
  9. print(List) # 輸出列表中的內容

  10. if __name__ == '__main__':
  11. manager = Manager() # 生成Manager對象
  12. Dict = manager.dict() # 生成一個可以在多個進程之間傳遞共享的字典
  13. List = manager.list() # 生成一個字典

  14. ProcessList = [] # 創建一個空列表,存放進程的對象,等待子進程執行用於

  15. for i in range(10): # 生成是個子進程
  16. p = Process(target=ChildProcess, args=(Dict, List)) # 創建一個子進程
  17. p.start() # 啟動
  18. ProcessList.append(p) # 把子進程添加到p_list列表中

  19. for res in ProcessList: # 循環所有的子進程
  20. res.join() # 等待執行完畢
  21. print('\n')
  22. print(Dict)
  23. print(List)

輸出結果

  1. C:\Python\Python35\python.exe E:/MyCodeProjects/多進程/s4.py
  2. [5112]
  3. [5112, 3448]
  4. [5112, 3448, 4584]
  5. [5112, 3448, 4584, 2128]
  6. [5112, 3448, 4584, 2128, 11124]
  7. [5112, 3448, 4584, 2128, 11124, 10628]
  8. [5112, 3448, 4584, 2128, 11124, 10628, 5512]
  9. [5112, 3448, 4584, 2128, 11124, 10628, 5512, 10460]
  10. [5112, 3448, 4584, 2128, 11124, 10628, 5512, 10460, 10484]
  11. [5112, 3448, 4584, 2128, 11124, 10628, 5512, 10460, 10484, 6804]


  12. {'k1': 'v1', 'k2': 'v2'}
  13. [5112, 3448, 4584, 2128, 11124, 10628, 5512, 10460, 10484, 6804]

  14. Process finished with exit code 0

鎖(Lock)

  1. from multiprocessing import Process, Lock

  2. def ChildProcess(l, i):
  3. l.acquire() # 獲取鎖
  4. print('hello world', i)
  5. l.release() # 釋放鎖

  6. if __name__ == '__main__':
  7. lock = Lock() # 生成Lock對象
  8. for num in range(10):
  9. Process(target=ChildProcess, args=(lock, num)).start() # 創建並啟動一個子進程

進程池

同一時間啟動多少個進程

  1. #!/use/bin/env python
  2. # _*_ coding: utf-8 _*_

  3. from multiprocessing import Pool
  4. import time

  5. def myFun(i):
  6. time.sleep(2)
  7. return i+100

  8. def end_call(arg):
  9. print("end_call>>", arg)

  10. p = Pool(5) # 允許進程池內同時放入5個進程
  11. for i in range(10):
  12. p.apply_async(func=myFun, args=(i,),callback=end_call) # # 平行執行,callback是主進程來調用
  13. # p.apply(func=Foo) # 串行執行

  14. print("end")
  15. p.close()
  16. p.join() # 進程池中進程執行完畢後再關閉,如果註釋,那麼程序直接關閉。

線程池

簡單實現

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3. import threading
  4. import queue
  5. import time
  6. class MyThread:
  7. def __init__(self,max_num=10):
  8. self.queue = queue.Queue()
  9. for n in range(max_num):
  10. self.queue.put(threading.Thread)
  11. def get_thread(self):
  12. return self.queue.get()
  13. def put_thread(self):
  14. self.queue.put(threading.Thread)
  15. pool = MyThread(5)
  16. def RunThread(arg,pool):
  17. print(arg)
  18. time.sleep(2)
  19. pool.put_thread()
  20. for n in range(30):
  21. thread = pool.get_thread()
  22. t = thread(target=RunThread, args=(n,pool,))
  23. t.start()

複雜版本

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-

  3. import queue
  4. import threading
  5. import contextlib
  6. import time

  7. StopEvent = object()

  8. class ThreadPool(object):

  9. def __init__(self, max_num, max_task_num = None):
  10. if max_task_num:
  11. self.q = queue.Queue(max_task_num)
  12. else:
  13. self.q = queue.Queue()
  14. self.max_num = max_num
  15. self.cancel = False
  16. self.terminal = False
  17. self.generate_list = []
  18. self.free_list = []

  19. def run(self, func, args, callback=None):
  20. """
  21. 線程池執行一個任務
  22. :param func: 任務函數
  23. :param args: 任務函數所需參數
  24. :param callback: 任務執行失敗或成功後執行的回調函數,回調函數有兩個參數1、任務函數執行狀態;2、任務函數返回值(默認為None,即:不執行回調函數)
  25. :return: 如果線程池已經終止,則返回True否則None
  26. """
  27. if self.cancel:

  28. return
  29. if len(self.free_list) == 0 and len(self.generate_list) < self.max_num:
  30. self.generate_thread()
  31. w = (func, args, callback,)
  32. self.q.put(w)

  33. def generate_thread(self):
  34. """
  35. 創建一個線程
  36. """
  37. t = threading.Thread(target=self.call)
  38. t.start()

  39. def call(self):
  40. """
  41. 循環去獲取任務函數並執行任務函數
  42. """
  43. current_thread = threading.currentThread()
  44. self.generate_list.append(current_thread)

  45. event = self.q.get()
  46. while event != StopEvent:

  47. func, arguments, callback = event
  48. try:
  49. result = func(*arguments)
  50. success = True
  51. except Exception as e:
  52. success = False
  53. result = None

  54. if callback is not None:
  55. try:
  56. callback(success, result)
  57. except Exception as e:
  58. pass

  59. with self.worker_state(self.free_list, current_thread):
  60. if self.terminal:
  61. event = StopEvent
  62. else:
  63. event = self.q.get()
  64. else:

  65. self.generate_list.remove(current_thread)

  66. def close(self):
  67. """
  68. 執行完所有的任務後,所有線程停止
  69. """
  70. self.cancel = True
  71. full_size = len(self.generate_list)
  72. while full_size:
  73. self.q.put(StopEvent)
  74. full_size -= 1

  75. def terminate(self):
  76. """
  77. 無論是否還有任務,終止線程
  78. """
  79. self.terminal = True

  80. while self.generate_list:
  81. self.q.put(StopEvent)

  82. self.q.queue.clear()

  83. @contextlib.contextmanager
  84. def worker_state(self, state_list, worker_thread):
  85. """
  86. 用於記錄線程中正在等待的線程數
  87. """
  88. state_list.append(worker_thread)
  89. try:
  90. yield
  91. finally:
  92. state_list.remove(worker_thread)

  93. # How to use

  94. pool = ThreadPool(5)

  95. def callback(status, result):
  96. # status, execute action status
  97. # result, execute action return value
  98. pass

  99. def action(i):
  100. print(i)

  101. for i in range(30):
  102. ret = pool.run(action, (i,), callback)

  103. time.sleep(5)
  104. print(len(pool.generate_list), len(pool.free_list))
  105. print(len(pool.generate_list), len(pool.free_list))
  106. pool.close()
  107. pool.terminate()

什麼是IO密集型和CPU密集型?

IO密集型(I/O bound)

頻繁網絡傳輸、讀取硬盤及其他IO設備稱之為IO密集型,最簡單的就是硬盤存取數據,IO操作並不會涉及到CPU。

計算密集型(CPU bound)

程序大部分在做計算、邏輯判斷、循環導致cpu佔用率很高的情況,稱之為計算密集型,比如說python程序中執行了一段代碼1+1,這就是在計算1+1的值

What is the association?

Python三大“程”!線程,進程和協程詳解!這還學不會?不存在的

協程的優缺點:

優點

  1. 無需線程上下文切換的開銷
  2. 無需原子操作鎖定及同步的開銷(更改一個變量)
  3. 方便切換控制流,簡化編程模型
  4. 高併發+高擴展性+低成本:一個CPU支持上萬的協程都不是問題。所以很適合用於高併發處理。

缺點:

  1. 無法利用多核資源:協程的本質是個單線程,它不能多核,協程需要和進程配合才能運行在多CPU上,當然我們日常所編寫的絕大部分應用都沒有這個必要,除非是CPU密集型應用。
  2. 進行阻塞(Blocking)操作(如IO時)會阻塞掉整個程序

實現協程實例

yield

  1. def consumer(name):
  2. print("--->starting eating baozi...")
  3. while True:
  4. new_baozi = yield # 直接返回
  5. print("[%s] is eating baozi %s" % (name, new_baozi))

  6. def producer():
  7. r = con.__next__()
  8. r = con2.__next__()
  9. n = 0
  10. while n < 5:
  11. n += 1
  12. con.send(n) # 喚醒生成器的同時傳入一個參數
  13. con2.send(n)
  14. print("\033[32;1m[producer]\033[0m is making baozi %s" % n)

  15. if __name__ == '__main__':
  16. con = consumer("c1")
  17. con2 = consumer("c2")
  18. p = producer()

Greenlet

安裝greenlet

  1. pip3 install greenlet
  2. # -*- coding:utf-8 -*-
  3. from greenlet import greenlet

  4. def func1():
  5. print(12)
  6. gr2.switch()
  7. print(34)
  8. gr2.switch()

  9. def func2():
  10. print(56)
  11. gr1.switch()
  12. print(78)

  13. # 創建兩個攜程
  14. gr1 = greenlet(func1)
  15. gr2 = greenlet(func2)
  16. gr1.switch() # 手動切換

Gevent

Gevent可以實現併發同步或異步編程,在gevent中用到的主要模式是Greenlet, 它是以C擴展模塊形式接入Python的輕量級協程,Greenlet全部運行在主程序操作系統進程的內部,但它們被協作式地調度。

安裝Gevent

  1. pip3 install gevent
  2. import gevent

  3. def foo():
  4. print('Running in foo')
  5. gevent.sleep(2)
  6. print('Explicit context switch to foo again')

  7. def bar():
  8. print('Explicit context to bar')
  9. gevent.sleep(3)
  10. print('Implicit context switch back to bar')

  11. # 自動切換
  12. gevent.joinall([
  13. gevent.spawn(foo), # 啟動一個協程
  14. gevent.spawn(bar),
  15. ])

頁面抓取

  1. from urllib import request
  2. from gevent import monkey
  3. import gevent
  4. import time

  5. monkey.patch_all() # 當前程序中只要設置到IO操作的都做上標記

  6. def wget(url):
  7. print('GET: %s' % url)
  8. resp = request.urlopen(url)
  9. data = resp.read()
  10. print('%d bytes received from %s.' % (len(data), url))

  11. urls = [
  12. 'https://www.python.org/',
  13. 'https://www.python.org/',
  14. 'https://github.com/',
  15. 'https://yw666.blog.51cto.com/',
  16. ]

  17. # 串行抓取
  18. start_time = time.time()
  19. for n in urls:
  20. wget(n)
  21. print("串行抓取使用時間:", time.time() - start_time)

  22. # 並行抓取
  23. ctrip_time = time.time()
  24. gevent.joinall([
  25. gevent.spawn(wget, 'https://www.python.org/'),
  26. gevent.spawn(wget, 'https://www.python.org/'),
  27. gevent.spawn(wget, 'https://github.com/'),
  28. gevent.spawn(wget, 'https://yw666.blog.51cto.com/'),
  29. ])
  30. print("並行抓取使用時間:", time.time() - ctrip_time)

輸出

  1. C:\Python\Python35\python.exe E:/MyCodeProjects/協程/s4.py
  2. GET: https://www.python.org/
  3. 47424 bytes received from https://www.python.org/.
  4. GET: https://www.python.org/
  5. 47424 bytes received from https://www.python.org/.
  6. GET: https://github.com/
  7. 25735 bytes received from https://github.com/.
  8. GET: https://blog.ansheng.me/
  9. 82693 bytes received from https://yw666.blog.51cto.com/.
  10. 串行抓取使用時間: 15.143015384674072
  11. GET: https://www.python.org/
  12. GET: https://www.python.org/
  13. GET: https://github.com/
  14. GET: https://blog.ansheng.me/
  15. 25736 bytes received from https://github.com/.
  16. 47424 bytes received from https://www.python.org/.
  17. 82693 bytes received from https://yw666.blog.51cto.com/.
  18. 47424 bytes received from https://www.python.org/.
  19. 並行抓取使用時間: 3.781306266784668

  20. Process finished with exit code 0
Python三大“程”!線程,進程和協程詳解!這還學不會?不存在的


分享到:


相關文章: