背景
在我們的工作中,分佈式的生產環境要求我們提高測試效率。原先的上線job串行執行,嚴重製約了我們的上線效率。
我們從兩個層面來解決問題:jenkins job層面,設置裏面勾選Execute concurrent builds if necessary來實現多job並行。腳本層面,引入python併發來解決腳本內串行的問題。
取捨
關於多線程還是多進程的取捨。
如果是IO密集型,線程和進程都可以,相對而言,線程稍複雜。
如果是cpu密集型,那麼多進程更合理。
線程
線程模型如下:
做一個線程池,總數爲cpu數加1,每一個子類無限循環。
# coding=utf-8
from Queue import Queue
from threading import Thread
from single import *
class ProcessWorker(Thread):
def __init__(self, queue):
Thread.__init__(self)
self.queue = queue
def run(self):
while True:
# Get the work from the queue
num = self.queue.get()
processNum(num)
self.queue.task_done()
def main():
ts = time()
nums = getNums(4)
# Create a queue to communicate with the worker threads
queue = Queue()
# Create 4 worker threads
for x in range(4):
worker = ProcessWorker(queue)
# Setting daemon to True will let the main thread exit even though the workers are blocking
worker.daemon = True
worker.start()
# Put the tasks into the queue
for num in nums:
queue.put(num)
# Causes the main thread to wait for the queue to finish processing all the tasks
queue.join()
print("cost time is: {:.2f}s".format(time() - ts))
if __name__ == "__main__":
main()
進程
每當線程中的一個準備工作時,進程可以不斷轉換線程。使用Python或其他有GIL的解釋型語言中的線程模塊實際上會降低性能。如果你的代碼執行的是CPU密集型的任務,例如解壓gzip文件,使用線程模塊將會導致執行時間變長。對於CPU密集型任務和真正的並行執行,我們可以使用多進程(multiprocessing)模塊。
官方的Python實現——CPython——帶有GIL.
爲了使用多進程,我們得建立一個多進程池。通過它提供的map方法,我們把URL列表傳給池。
# coding=utf-8
from functools import partial
from multiprocessing.pool import Pool
from single import *
from time import time
def main():
ts = time()
nums = getNums(4)
p = Pool(4)
p.map(processNum, nums)
print("cost time is: {:.2f}s".format(time() - ts))
if __name__ == "__main__":
main()
關於併發和並行的理解
併發是指,程序在運行的過程中存在多於一個的執行上下文。這些執行上下文一般對應着不同的調用棧。
在單處理器上,併發程序雖然有多個上下文運行環境,但某一個時刻只有一個任務在運行。
但在多處理器上,因爲有了多個執行單元,就可以同時有數個任務在跑。
這種物理上同一時刻有多個任務同時運行的方式就是並行。
和併發相比,並行更加強調多個任務同時在運行。
而且並行還有一個層次問題,比如是指令間的並行還是任務間的並行。