分享

极简python教程05:进程和线程

 程序员小谭 2022-03-02

错过往期内容?试试点击下方蓝色字体
测试基础系列  实战项目系列
技能提升系列  自动化测试系列
职场碎碎念系列
tips:
01 加群、加组织,请点击这里
02 获取测试资料,请点击这里

大家好,我是测试奇谭的作者谭叔。

-->还没加我微信的小伙伴,点这里加我微信吧<--

讲解之前,我先说说我的教程和网上其他教程的区别:

1 我分享的是我在工作中高频使用的场景,是精华内容

2 你可以敲代码学习,效果最佳;亦可以收藏,待有需求时粘来即用

这一场,主讲python的进程和线程

目的:掌握初学必须的进程和线程知识。

进程和线程的区别和联系

终于开始加深难度,来到进程和线程的知识点~

单就这两个概念,就难倒过不少初学者——今天学了概念,明天就忘记;明天学了例子,又忘记了概念。

要理解进程和线程的联系和区别,我举个特简单的例子:

你的电脑有两个浏览器,一个谷歌浏览器,一个qq浏览器。

一个浏览器就是一个进程。

然后,你打开了谷歌浏览器,百度搜索了测试奇谭,又新开一个标签页,打开谭叔的文章,如下图所示:

你可以这样理解——在同一个浏览器打开的两个网页就是两个线程。如果我关闭了浏览器,这两个线程也没有了。

好了,当你有了概念后,我才好讲两者的区别和联系。

进程

  • 优点

    • 稳定性高,当程序崩溃后,不影响其他进程的使用。即谷歌浏览器崩溃后,qq浏览器还可以正常使用。

  • 缺点

    • 创建进程的代价大。即当你开了各种各样的浏览器后,你的电脑会特别卡,因为电脑内存和CPU有上限。

线程

  • 优点

    • 多线程通常比多进程快一点,但优势不大

  • 缺点

    • 任何一个线程挂掉会造成整个进程崩溃,因为线程共享进程的内存(浏览器的例子不再适用,可以理解为绑在一条船上的蚂蚱)

多进程

因为大多数小伙伴用的Windows操作系统,所以针对Unix/Linux的fork()调用抛开不谈。
在python,一般使用multiprocessing实现多进程。
import osfrom multiprocessing import Process
def run_proc(name): print('开始执行子进程 %s (%s)…' % (name, os.getpid()))
# 子进程要执行的代码if __name__ == '__main__': print('父进程 %s.' % os.getpid()) p = Process(target=run_proc, args=('test',)) # 创建一个Process实例 print('子进程即将开始.') p.start() # 用start()方法启动实例 p.join() # join()方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步 print('子进程结束.')
要细讲吗?
其实,我的备注写得很全,你只需copy代码下来执行一次,或者debug一次,就能明白。

线程池

如何理解?
即代码可运行的进程数量,有个地方管控它。
from multiprocessing import Poolimport osimport timeimport random
def long_time_task(name): print('开始 task %s (%s)...' % (name, os.getpid())) start = time.time() time.sleep(random.random() * 3) end = time.time() print('Task %s 执行花费 %0.2f 秒.' % (name, (end - start)))if __name__ == '__main__': print('父亲进程 %s.' % os.getpid()) p = Pool(3) # 因为Pool的默认大小是4,故task 0,1,2,3是立刻执行的,而task 4要等待前面某个task完成后才执行,但最多同时执行4个进程 # 由于Pool的默认大小是CPU的核数,如果你拥有8核CPU,要提交至少9个子进程才能看到等待效果 for i in range(5): p.apply_async(long_time_task, args=(i,)) print('等待子进程结束...') p.close() # 对Pool对象调用join()方法会等待所有子进程执行完毕 # 调用join()之前必须先调用close(),调用close()之后就不能继续添加新的Process了 p.join() print('所有子进程已执行.')
以上,是必知必会的。
当然,最好的学习时机是:当你要用时,再来复盘学,效果最佳。
如果你学了,没有使用场景,我建议缓一缓学或者作为知识储备。

多线程

多线程有三点必须提前明确:

  • 多任务需求可以由多进程完成,也可以由一个进程内的多线程完成

  • 进程是由若干线程组成

  • 一个进程至少有一个线程

Python的标准库提供了两个模块:_thread和threading,_thread是低级模块,我们一般使用高级模块threading(对_thread进行过封装)。

启动一个线程就是把一个函数传入并创建Thread实例,然后调用start()开始执行,我们看一个简单的例子:

import timeimport threading
# 新线程执行的代码def loop(): # 由于任何进程默认就会启动一个线程,我们把该线程称为主线程,主线程又可以启动新的线程, # Python的threading模块有个current_thread()函数,它永远返回当前线程的实例 print('线程ss %s 运行中…' % threading.current_thread().name) n = 0 # 主线程实例的名字叫MainThread,子线程的名字在创建时指定,我们用LoopThread命名子线程,在打印输出的时候显示名字 while n < 5: n = n + 1 print('线程ss %s >>> %s' % (threading.current_thread().name, n)) time.sleep(1)
print('线程ss %s 已结束.' % threading.current_thread().name)print('线程 %s is 运行中…' % threading.current_thread().name)
t = threading.Thread(target=loop, name='LoopThread')t.start()t.join()
'''对于 join()方法而言,其另一个重要方面是其实它根本不需要调用。一旦线程启动,它们 就会一直执行,直到给定的函数完成后退出。如果主线程还有其他事情要去做,而不是等待这些线程完成(例如其他处理或者等待新的客户端请求),就可以不调用 join()。join()方法只有在你需要等待线程完成的时候才是有用的'''
print('线程 %s 已结束.' % threading.current_thread().name)
同样,以上内容,是必知必会的。
并且,工作场景,我们一般会使用多线程处理问题,而非多进程。(注意:是一般)
至于进程间通信、线程锁、GIL锁、多线程变量、线程间通信、异步协程等知识,讲起来比较复杂,也不是极简教程的核心,你可以先自学,或者当你真正要使用它的时候再去看,再去学。

一如既往,做个总结

01 多线程、多进程,是必知必会的;

02 学习进度自己琢磨,既不能死磕,亦不能简单跳过;

03 小伙伴比较关心,面试时会不会被问到。答:一般公司不会,so?

封面图片来自unsplash.com

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多