0. 简介
线程和进程的区别:
进程是资源分配的最小单位,线程是CPU调度的最小单位。
也就是说,线程是在进程的概念下的,进程完成一个大的工作,线程则完成其中的小任务。进程间可能加互斥锁,而线程的内存则容易共享等。
Q1:为什么不推荐使用python的多线程
A1:对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同时只有一个线程在运行。
在多线程环境中,Python虚拟机按照以下方式执行。
1.设置GIL。
2.切换到一个线程去执行。
3.运行。
4.把线程设置为睡眠状态。
5.解锁GIL。
6.再次重复以上步骤。
对所有面向I/O的(会调用内建的操作系统C代码的)程序来说,GIL会在这个I/O调用之前被释放,以允许其他线程在这个线程等待I/O的时候运行。如果某线程并未使用很多I/O操作,它会在自己的时间片内一直占用处理器和GIL。也就是说,I/O密集型的Python程序比计算密集型的Python程序更能充分利用多线程的好处。
Python不一样,**它不管你有几个核,单位时间多个核只能跑一个线程,然后时间片轮转。**看起来很不可思议?但是这就是GIL搞的鬼。任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。
GIL
GIL 是python的全局解释器锁,同一进程中假如有多个线程运行,一个线程在运行python程序的时候会霸占python解释器(加了一把锁即GIL),使该进程内的其他线程无法运行,等该线程运行完后其他线程才能运行。如果线程运行过程中遇到耗时操作,则解释器锁解开,使其他线程运行。所以在多线程中,线程的运行仍是有先后顺序的,并不是同时进行。
多进程中因为每个进程都能被系统分配资源,相当于每个进程有了一个python解释器,所以多进程可以实现多个进程的同时运行,缺点是进程系统资源开销大
Q3:简述多线程、多进程
进程:
1、操作系统进行资源分配和调度的基本单位,多个进程之间相互独立
2、稳定性好,如果一个进程崩溃,不影响其他进程,但是进程消耗资源大,开启的进程数量有限制
线程:
1、CPU进行资源分配和调度的基本单位,线程是进程的一部分,是比进程更小的能独立运行的基本单位,一个进程下的多个线程可以共享该进程的所有资源
2、如果IO操作密集,则可以多线程运行效率高,缺点是如果一个线程崩溃,都会造成进程的崩溃
应用:
IO密集的用多线程,在用户输入,sleep 时候,可以切换到其他线程执行,减少等待的时间;
CPU密集的用多进程,因为假如IO操作少,用多线程的话,因为线程共享一个全局解释器锁,当前运行的线程会霸占GIL,其他线程没有GIL,就不能充分利用多核CPU的优势。
1. 多线程
# 多线程
import threading
def haha(max_num, k = 0, j = 3):
for i in range(max_num + k + j):
time.sleep(1)
print(i)
return i
for x in range(3):
n = x + 3
t = threading.Thread(target=haha,args=(n,1,))
#也可以干脆不写这一行
t.setDaemon(False)
t.start()
2. 多进程
from multiprocessing import Pool,Manager
import os, time, random
def long_time_task(name):
print('Run task %s (%s)...' % (name, os.getpid()))
return_dict = {}
start = time.time()
# time.sleep(random.random() * 1)
return_dict[name] = [33,44]
end = time.time()
print('Task %s runs %0.2f seconds.' % (name, (end - start)))
return return_dict
if __name__=='__main__':
manager = Manager()
return_dict = manager.dict()
result = []
print ('Parent process %s.' % os.getpid())
p = Pool(processes = 4)
for i in range(8):
result.append(p.apply_async(long_time_task, args=(i,)))
print ('Waiting for all subprocesses done...')
p.close()
p.join()
print ('All subprocesses done.')
for r in result:
print(r.get())
3. 多进程 + 多线程
import threading
import numpy as np
from multiprocessing import Pool
def pr(n, flist):
print('pr')
for i in flist:
print('name {}, i {}'.format(n, i))
def process(n, flist):
for i in flist:
t = threading.Thread(target = pr, args = (n, np.arange(i, i+1)))
t.start()
# pr(n, np.arange(i, i + 1))
p = Pool()
print(os.getpid())
for name in ['a', 'b', 'c']:
print('name',name)
p.apply_async(process, args = (name, np.arange(1,5)))
p.close()
p.join()
结果:
27878
name a
name b
name prc
prprprpr
prprprpr
pr
name a, i 3pr
name a, i 4
name b, i 3name a, i 5name b, i 4name b, i 5
prname b, i 2
name a, i 2
name c, i 2
name c, i 5name c, i 3
name c, i 4
如何先多线程,再多进程的话,
%%time
import threading
import numpy as np
from multiprocessing import Pool
def pr(n, flist):
print('pr')
for i in flist:
i += 1
print('name {}, i {}'.format(n, i))
def process(n, flist):
p = Pool()
print(os.getpid())
for i in flist:
p.apply_async(pr, args = (n, np.arange(i, i+1)))
p.close()
p.join()
for name in ['a', 'b', 'c']:
print('name',name)
t = threading.Thread(target = process, args = (name, np.arange(1, 5)))
t.start()
结果:
name a
name b
name c
CPU times: user 16 ms, sys: 2.59 s, total: 2.61 s
Wall time: 2.6 s
27878
prprprpr
name a, i 5name a, i 2name a, i 4name a, i 3
27878
prprpr
pr
name b, i 4name b, i 2name b, i 3
name b, i 5
27878
pr
pr
prprname c, i 2
name c, i 3name c, i 4name c, i 5
好像python的多线程是伪多线程,只是交替进行线程运行。
4. 嵌套多进程
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import multiprocessing
# We must import this explicitly, it is not imported by the top-level
# multiprocessing module.
import multiprocessing.pool
import time
from random import randint
class NoDaemonProcess(multiprocessing.Process):
# make 'daemon' attribute always return False
def _get_daemon(self):
return False
def _set_daemon(self, value):
pass
daemon = property(_get_daemon, _set_daemon)
# We sub-class multiprocessing.pool.Pool instead of multiprocessing.Pool
# because the latter is only a wrapper function, not a proper class.
class MyPool(multiprocessing.pool.Pool):
Process = NoDaemonProcess
def sleepawhile(t):
print("Sleeping %i seconds..." % t)
time.sleep(t)
return t
def work(num_procs):
print("Creating %i (daemon) workers and jobs in child." % num_procs)
pool = multiprocessing.Pool(num_procs)
result = pool.map(sleepawhile,
[randint(1, 5) for x in range(num_procs)])
# The following is not really needed, since the (daemon) workers of the
# child's pool are killed when the child is terminated, but it's good
# practice to cleanup after ourselves anyway.
pool.close()
pool.join()
return result
def test(n):
print("run",n)
print("Creating 5 (non-daemon) workers and jobs in main process.")
pool = MyPool(5)
result = pool.map(work, [randint(1, 5) for x in range(5)])
pool.close()
pool.join()
print(result)
if __name__ == '__main__':
test(1)
print("run again.")
test(2)