Python — это язык, который выполняется в интерпретаторе. Согласно исследованиям, в Python есть глобальная блокировка (GIL). При использовании многопоточности (Thread) нельзя задействовать многоядерность. Однако использование многопроцессности (Multiprocess) позволяет задействовать преимущества многоядерности и действительно повысить эффективность.
Сравнительный эксперимент
Согласно информации, если многопоточный процесс являетсятребовательным к ЦП, то многопоточность не может значительно повысить эффективность; наоборот, она может даже снизить эффективность из-за частого переключения потоков, поэтому рекомендуется многопроцессность; если онинтенсивен по вводу-выводу, то многопоточные процессы могут использовать время простоя во время ожидания блокировки ввода-вывода для выполнения других потоков, повышая эффективность. Поэтому мы сравниваем эффективность различных сценариев с помощью экспериментов.
| Операционная система | Процессор | Память | Жёсткий диск |
|---|---|---|---|
| Windows 10 | Двухъядерный | 8GB | Механический жёсткий диск |
(1) Импортируйте необходимые модули
import requests import time from threading import Thread from multiprocessing import Process
(2) Определите функцию вычислений, интенсивно использующую CPU
def count(x, y):
# 使程序完成50万计算
c = 0
while c < 500000:
c += 1
x += x
y += y
(3) Определите IO-интенсивную функцию чтения/записи файлов
def write():
f = open("test.txt", "w")
for x in range(5000000):
f.write("testwrite\n")
f.close()
def read():
f = open("test.txt", "r")
lines = f.readlines()
f.close()
(4) Определите функцию сетевого запроса
_head = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36'}
url = "http://www.tieba.com"
def http_request():
try:
webPage = requests.get(url, headers=_head)
html = webPage.text
return {"context": html}
except Exception as e:
return {"error": e}
(5) Проверьте время, необходимое для линейного выполнения IO-интенсивных операций, CPU-интенсивных операций и операций, интенсивно использующих сетевые запросы
# CPU密集操作
t = time.time()
for x in range(10):
count(1, 1)
print("Line cpu", time.time() - t)
# IO密集操作
t = time.time()
for x in range(10):
write()
read()
print("Line IO", time.time() - t)
# 网络请求密集型操作
t = time.time()
for x in range(10):
http_request()
print("Line Http Request", time.time() - t)
Вывод
CPU-интенсивные: 95.6059999466, 91.57099986076355, 92.52800011634827, 99.96799993515015
IO-интенсивные: 24.25, 21.76699995994568, 21.769999980926514, 22.060999870300293
Интенсивные по сетевым запросам: 4.519999980926514, 8.563999891281128, 4.371000051498413, 4.522000074386597, 14.671000003814697
(6) Проверьте время, необходимое для одновременного многопоточного выполнения CPU-интенсивных операций
counts = []
t = time.time()
for x in range(10):
thread = Thread(target=count, args=(1,1))
counts.append(thread)
thread.start()
e = counts.__len__()
while True:
for th in counts:
if not th.is_alive():
e -= 1
if e <= 0:
break
print(time.time() - t)
Вывод: 25.69700002670288、24.02400016784668
(7) Проверьте время, необходимое для одновременного многопоточного выполнения IO-интенсивных операций
def io():
write()
read()
t = time.time()
ios = []
t = time.time()
for x in range(10):
thread = Thread(target=count, args=(1,1))
ios.append(thread)
thread.start()
e = ios.__len__()
while True:
for th in ios:
if not th.is_alive():
e -= 1
if e <= 0:
break
print(time.time() - t)
Вывод: 99.9240000248 、101.26400017738342、102.32200002670288
(8) Проверка времени, необходимого для параллельного многопоточного выполнения сетевых ресурсоёмких операций
t = time.time()
ios = []
t = time.time()
for x in range(10):
thread = Thread(target=http_request)
ios.append(thread)
thread.start()
e = ios.__len__()
while True:
for th in ios:
if not th.is_alive():
e -= 1
if e <= 0:
break
print("Thread Http Request", time.time() - t)
Вывод: 0.7419998645782471、0.3839998245239258、0.3900001049041748
(9) Проверка времени, необходимого для параллельного многопроцессного выполнения операций, интенсивно использующих CPU
counts = []
t = time.time()
for x in range(10):
process = Process(target=count, args=(1,1))
counts.append(process)
process.start()
e = counts.__len__()
while True:
for th in counts:
if not th.is_alive():
e -= 1
if e <= 0:
break
print("Multiprocess cpu", time.time() - t)
Вывод: 54.342000007629395、53.437999963760376
(10) Проверка параллельного многопроцессного выполнения операций с интенсивным вводом-выводом
t = time.time()
ios = []
t = time.time()
for x in range(10):
process = Process(target=io)
ios.append(process)
process.start()
e = ios.__len__()
while True:
for th in ios:
if not th.is_alive():
e -= 1
if e <= 0:
break
print("Multiprocess IO", time.time() - t)
Вывод: 12.509000062942505、13.059000015258789
(11) Проверка параллельного многопроцессного выполнения операций с интенсивными HTTP-запросами
t = time.time()
httprs = []
t = time.time()
for x in range(10):
process = Process(target=http_request)
ios.append(process)
process.start()
e = httprs.__len__()
while True:
for th in httprs:
if not th.is_alive():
e -= 1
if e <= 0:
break
print("Multiprocess Http Request", time.time() - t)
Вывод: 0.5329999923706055、0.4760000705718994
Результаты эксперимента
| Операции, интенсивно использующие CPU | Операции с интенсивным вводом-выводом | Операции с интенсивными сетевыми запросами | |
|---|---|---|---|
| Линейные операции | 94.91824996469 | 22.46199995279 | 7.3296000004 |
| Многопоточные операции | 101.1700000762 | 24.8605000973 | 0.5053332647 |
| Многопроцессные операции | 53.8899999857 | 12.7840000391 | 0.5045000315 |
Из приведённых выше результатов видно:
Многопоточность, похоже, также не имеет значительного преимущества в IO-интенсивных операциях (возможно, если бы задачи ввода-вывода были тяжелее, преимущество проявилось бы). В CPU-интенсивных операциях она явно уступает однопоточному линейному выполнению. Однако для таких операций, как сетевые запросы, которые вызывают блокировку потоков в режиме активного ожидания, многопоточность имеет очень значительное преимущество.
Многопроцессность показывает преимущества в производительности в сценариях CPU-интенсивных, IO-интенсивных и интенсивных по сетевым запросам (операциях, где часто возникает блокировка потоков). Однако для операций, похожих на интенсивные по сетевым запросам, она почти такая же, как многопоточность, но потребляет больше ресурсов, таких как CPU. Поэтому в этом случае мы можем выбрать многопоточность для выполнения.

Исходный адрес: http://blog.atomicer.cn/2016/09/30/Python%E4%B8%AD%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%92%8C%E5%A4%9A%E8%BF%9B%E7%A8%8B%E7%9A%84%E5%AF%B9%E6%AF%94/