这个代码的运行效果:
这个主进程先结束,你觉得好吗?
肯定不好,就好比:你打开一个浏览器(开启了一个进程),接着在浏览器中打算下载一些文件或者软件。(若干个子进程),结果:浏览器意外关闭,下载中断。即:子进程未执行完,但是主进程先结束了。
肯定不好。
这时候,就会有一个方法出来解决这个问题:join( )
如下段代码所示:
第二十一行代码:p1.join( ) # 主进程等待子进程结束
运行结果:
这样,当子进程结束后,主进程才跟着结束。
场景1:
网页中下载东西,如果关闭网页则会弹出提示,那么这就是我们主进程需要等待子进程执行完毕后再关闭。
场景2:
terminante( )强制终止子进程
第22行:
子进程被强制结束。
主进程则跟着结束了。
使用if-else-elif判断:
运行结果:
注意,这个强制终止方法只有进程才有。
守护模式:daemon=True
与强制结束子进程的terminante( )的作用类似。
第18行的代码:daemon = True
但是有区别:terminate( )方法是强制结束,守护进程方法daemon是自行结束。
生命周期:is_alive( ),这个方法是判断子进程是否处于生命周期中。(通俗点就是判断子进程是否开启)
True表示处于生命周期中,False则表示未处于。
如下面这个例子:(第19、21行代码末端)
输出结果:
多任务标识
pid:获取子进程的id号
ident:获取线程id
扩展:
线程注意点:
1.进程之间的执行是无序的
运行结果:
2.主线程会等待所有的子线程执行结束再结束
3.进程之间不会共享全局变量
运行结果:
线程注意点:
1.线程之间的执行是无序的
运行结果:
2.主线程会等待所有的子线程结束才会结束
运行结果:
虽然主进程运行时间只有1秒,但是多出来的一秒会再次执行一次。
所以这里的运行结果是“任务执行中...”执行4次。
当把time.sleep(1)修改为:time.sleep(0.9)后,子线程会执行3次,之后主线程会自动结束。
3.与进程不同,线程共享全局变量
运行结果:
4.线程之间共享全局变量数据出现错误问题
运行结果:
这是运算量不大的情况下,可以应付。
但是如果加大数据量:
运行结果:
这是线程之间抢占资源的结果,线程t1、t2之间执行顺序是无序的。
所以,即使多次执行这段代码,结果都很难是正确的。
解决线程之间抢占资源方式:
1.线程等待
线程等待的示例代码
运行结果:
但这就是一个单任务了。
2.互斥锁
对共享数据进行锁定,保证同一时刻只能有一个线程去操作
注意:
互斥锁是多个线程一起去抢,抢到锁的线程先执行,没抢到的线程则需要等待。
等互斥锁使用完释放后,其他等待的线程再去抢这个锁。
threading模块中定义了Lock变量,这个变量本质上就是一个函数。
通过调用这个函数可以获取一把互斥锁。
#创建锁
mutex = threading.Lock( )
#上锁
mutex.acquire( )
#释放锁
mutex.realease( )
死锁:一直等待对方释放的情景就死锁。
导致的结果:会造成应用程序的停止响应,不能再处理其他任务了。
运行结果:
使用互斥锁时,要注意死锁的问题。
死锁一旦产生,就会导致应用程序的停止响应,应用程序就无法执行了。
进程与线程对比
1.关系对比
1.线程是依附在进程里面的,没有进程就没有线程
2.一个进程默认提供一条线程,进程可以创建多个线程
2.区别对比
1.进程之间不共享全局全变量
2.线程之间共享全局变量,但是要注意资源竞争的问题,解决办法:互斥锁或者线程同步
3.创建进程的资源的开销要比创建线程的资源开销大
4.进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位
5.线程不能狗独立执行,必须依附在进程中
6.多进程开发比单进程多线程开发稳定性要强
3.优胜点对比
进程优缺点
优点:可以用多核
缺点:资源开销大
线程优缺点
优点:资源开销小
缺点:不能使用多核