并发:多个进程在某个时间,一起运行
并行:多个进程在某个时刻,一起运行
python中对并发编程的支持
多线程:threading模块,利用CPU和IO可以同时执行的原理,让CPU不会干巴巴的等待IO完成
多进程:multiprocessing模块,利用多核CPU的能力,真正的并行执行任务
进程池和线程池
异步IO:asyncio模块,在单线程利用CPU和IO同时执行的原理,实现函数异步执行
多线程:
优点:相比进程,更加轻量级,占用资源少
缺点:相比协程,启动数目有限,占用内存资源,有线程切换开销
适用于:IO密集型计算,同时运行的任务数目要求不多
IO密集型:IO密集型指的是系统运作大部分的状况是CPU在等待I/O,
例如一些磁盘、内存、网络的读写,这种状况,CPU占用率不高,系统IO特别高。
典型的示例:文件处理程序,网络爬虫,读写数据库等
多进程
优点:可以利用多核CPU并行运算
缺点:占用资源最多,可启动数目比线程少
适用于:CPU密集型计算场景
CPU密集型:CPU密集型也叫计算密集型,是指I/O在很短的时间就可以完成,
CPU需要大量的计算和处理,特点是CPU占用率特别高。
典型的示例:压缩、解压缩,加密解密,正则表达式搜索等
多协程
优点:内存开销最少,启动协程数可以非常多
缺点:支持的库有限,例如不能使用requests模块,代码实现复杂
适用于:IO密集型计算、需要超多任务运行,有现成库支持的场景
进程
代码实现
from multiprocessing import Process
import os
import time
def run_proc(name):
print('Run child process name:%s (child id:%s,parent id:%s) ...'
%(name,os.getpid(),os.getppid())
)
time.sleep(1)
if __name__ == '__main__':
print('Parent process %s'%os.getpid())
argsl = ['z','l', 'y', 'd']
for i in argsl:
p = Process(target=run_proc,args=(i,)) # 轮询执行子进程
print('Child process will start {}'.format(i))
p.start() # 启动子进程
p.join()
# join方法等待子进程结束后继续往下执行,否则启动子进程后不会等待进程结束,
直接往下执行
print('Child process end')
进程池
进程池的概念,定义一个池子,在里面放上固定数量的进程,有需求来了,
就拿一个池中的进程来处理任务,等到处理完毕,进程并不关闭,
而是将进程再放回进程池中继续等待任务。如果有很多任务需要执行,池中的进程数量不够,
任务就要等待之前的进程执行任务完毕归来,拿到空闲进程才能继续执行。
代码实现
from multiprocessing import Pool
import os
def run(id):
print("parebt is {} and child id is {}".format(id,os.getpid()))
if __name__ == '__main__':
pl= Pool(10)
res=[]
for i in range(10):
res.append(pl.apply_async(run,args=(os.getpid(),)))
g = [i.get for i in res]
pl.close()
pl.join()
相关参数理论知识:
1、apply(func[, args[, kwds]]) :
apply用于传递不定参数,同python中的apply函数一致
(不过内置的apply函数从2.3以后就不建议使用了),主进程会阻塞于函数。
2、apply_async(func[, args[, kwds[, callback]]]) :
与apply用法一致,但它是非阻塞的且支持结果返回后进行回调。
主进程循环运行过程中不等待apply_async的返回结果,
在主进程结束后,即使子进程还未返回整个程序也会退出。
虽然 apply_async是非阻塞的,但其返回结果的get方法却是阻塞的,
如使用result.get()会阻塞主进程。如果我们对返回结果不感兴趣,
那么可以在主进程中使用pool.close与pool.join来防止主进程退出。
注意join方法一定要在close或terminate之后调用。
3、map(func, iterable[, chunksize]) :
map方法与在功能上等价与内置的map(),只不过单个任务会并行运行。
它会使进程阻塞直到结果返回。
但需注意的是其第二个参数虽然描述的为iterable,
但在实际使用中发现只有在整个队列全部就绪后,程序才会运行子进程。
4、map_async(func, iterable[, chunksize[, callback]]) :
与map用法一致,但是它是非阻塞的。其有关事项见apply_async。
5、imap(func, iterable[, chunksize]) :
与map不同的是, imap的返回结果为iter,
需要在主进程中主动使用next来驱动子进程的调用。
即使子进程没有返回结果,主进程对于gen_list(l)的 iter还是会继续进行,
6、imap_unordered(func, iterable[, chunksize]) :
同imap一致,只不过其并不保证返回结果与迭代传入的顺序一致。
7、close() :
关闭pool,使其不再接受新的任务。
8、terminate() :
结束工作进程,不再处理未处理的任务。
9、join() :
主进程阻塞等待子进程的退出, join方法要在close或terminate之后使用。
线程
import threading
import time,os
def single(id):
time.sleep(1)
print("single parebt is {} and child id is {}".format(id,os.getpid()))
def dance(id):
print("dance parebt is {} and child id is {}".format(id,os.getpid()))
if __name__ == '__main__':
t1 = threading.Thread(target=single,args=(1,))
t2 = threading.Thread(target=dance,args=(2,))
t1.start()
t2.start()
解决多线程无序问题
import threading
import random
from time import sleep
lock = threading.Lock()
list1 = [0] * 10
def task1():
lock.acquire()
for i in range(len(list1)):
print(list1)
list1[i] = 1
sleep(0.1)
lock.release()
def task2():
lock.acquire()
for i in range(len(list1)):
print('---->', list1[i])
sleep(0.1)
lock.release()
if __name__ == '__main__':
t1 = threading.Thread(target=task1)
t2 = threading.Thread(target=task2)
t1.start()
t2.start()
t1.join()
t2.join()
线程池
import time
from concurrent.futures import ThreadPoolExecutor
def get_thread_time(times):
time.sleep(times)
return times
# 创建线程池 指定最大容纳数量为4
executor = ThreadPoolExecutor(max_workers=4)
# 通过submit提交执行的函数到线程池中
task1 = executor.submit(get_thread_time, (1))
task2 = executor.submit(get_thread_time, (2))
print("task1:{} ".format(task1.done()))#false
print("task2:{}".format(task2.done()))#false
time.sleep(1)
print('after 1s {}'.format('-'*20))
done_map = {
"task1":task1.done(),#ture
"task2":task2.done()#false
}
# 2.5秒之后,线程的执行状态
for task_name,done in done_map.items():
if done:
print("{}:completed".format(task_name))#
协程
协程称之为函数中断,微线程
import time,asyncio
async def save_img(index, img_url):
path = "皮肤/" + img_url['name']
if not os.path.exists(path):
os.makedirs(path)
time.sleep(1)
content = requests.get(img_url['imgLink'], headers=headers).content
with open('./皮肤/' + img_url['name'] + '/' + img_url['skin_name']
+ str(index) + '.jpg', 'wb') as f:
f.write(content)
def main():
loop = asyncio.get_event_loop()# 创建事件循环对象
img_urls = get_img()
print(len(img_urls))
tasks = [save_img(img[0], img[1]) for img in enumerate(img_urls)]
try:
loop.run_until_complete(asyncio.wait(tasks))
# 注册加启动(等待结果返回)
finally:
loop.close()
if __name__ == '__main__':
start = time.time()
main()
end = time.time()
print(end - start)
yield
异步协程
异步相对同步而言,同步意味着有序,异步意味着无序,正因为异步的无序,使得各个程序间的协 调成为一大难题,
异步编程就是解决这一难题的编程,它是以进程、线程、协程、函数/方法作为执 行任务程序的基本单位,
结合回调、事件循环、信号量等机制,以提高程序整体执行效率和并发能 力的编程方式。
常见框架gevent的使用
本文来自博客园,作者:topass123,转载请注明原文链接:https://www.cnblogs.com/topass123/p/16840023.html