05python基础进阶之多任务与进程与线程

壹、多任务、进程、线程概念
一,多任务

	1、定义:同时运行多个程序任务;作用:提高程序运行效率;程序:占硬盘资源,静态概念;
	2,执行方式: 并发、并行
	并行:多个任务一起执行(核数大或等于任务数)
	并发:多个任务切换执行(核数小于任务数)
	
二,进程

        1,定义:一个运行起来的程序;拥有系统给分配CPU;系统分源‘基本单位’;
		2,作用:给程序的运行提供资源与环境

三,线程

        1,定义:一条进程内部的执行线索;系统独立调度和分派‘基本单元’;
		 2,作用: 用来执行代码;可开启多线程实现多任务
另:进程内部默认存在一线程 -- 主线程

贰、多进程与多线程

	一,创建流程:
			1、多进程创建
			        1、 导入进程包           import multiprocessing
			        2、创建子进程对象     p1 = multiprocessing.Process(target=函数名)
			        3、启动子进程p1        p1.start()
			        
			2. 子线程创建
			        1、导入threading模块         import threading
			        2、创建Thread类的对象     t1 = threading.Thread(target=函数名)
			        3、启动线程                        t1.start()
		
	 二,查看当前:
	查看当前进程: multiprocessing.current_process()
	查看当前线程:threading.current_thread() 
	获取当前进程编号:os.getpid() 、父进程 os.getppid())
	
	  三,参数传递
	
	    	1、给子进程传递参数
	def fn(a, b, name, age):
	        print(a)
	        print(b)
	        print(name)
	        print(age)
	p1 = multiprocessing.Process(target=fn1, args=(10, 20, 30, 40), kwargs={"name": "python", "age": 28})
	
	2、线程中Thread的参数
	        target参数    指定子线程运行的函数
	        args参数      指定子线程运行的函数所需的位置参数 -元组
	        kwargs参数    指定子线程运行的函数所需的关键字参数 -字典
	
	四,常用属性和方法
	        p1.name 获取进程名字
	        p1.start():启动子进程
	        p1.join():是否等待子进程执行结束
	        p1.terminate():不管任务是否完成,会终止子进程
	
	五、注意点
	 1、多进程注意点
	   		     a,多进程不共享全局变量
	        	     进程间是独立的空间。
	创建子进程会对主进程资源进行拷贝,也就是说子进程是主进程的一个副本,操作的不是同一进程里面的全局变量,只不过不同进程里面的全局变量名字相同。
	b、主进程不会等待所有的子进程执行结束再结束,除非手动join
	
	 	2、多线程的注意点:
	    		a,多个线程的执行顺序是无序的
	  b、子线程何时结束
	        	子线程把target指向的函数中的语句执行完毕后,或者线程中的函数代码执行完毕后,立即结束当前子线程 
	    		c、分配cpu给线程时,是通过时间片轮循方式进行的
	     
	   六、守护
	   1、守护主进程、主线程
	指主退出的时候子也结束:创建子的时候传入参数daemon=True
	   2,判断线程是否处于活动状态  t.is_alive()
	
	  七、多线程共享全局变量的问题和互斥锁的使用
	    1、多线程是共享全局变量
	    2、出现问题:因为多个线程的执行顺序是无序的,是靠竞争决定的,通俗地讲,谁抢到了,谁先执行,所以在使用全局变量的时候,数据结果可能会不正确
	    3、用互斥锁来解决
	        互斥锁的使用流程:
	        1、在主线程中,lock = threading.Lock(),开启子线程的时候当做位置参数传到线程中
	        2、在子线程中使用全局变量的时候申请锁
	            lock.acquire()   尝试获取这把锁,如果在这之前别的线程已经申请到了锁,则这里会阻塞等待,直到别的线程释放锁才继续往下执行,如果在这之前这把锁没有别的线程申请,则继续往下执行
	        3、修改全局变量
	        4、修改完全局变量,就应该释放锁
	            lock.release()   申请锁之后要释放锁别的线程才能去申请这把锁,使用这个全局变量
	
	     八,一些区别:
	1,存在多进程与多线程同时存在;
	2,主进程不会等待主线程结束才结束,主线程也不会等待子线程结束才结束,除非手动join,设置让主等待子结束再一起结束;
	3,子进程之间是相互独立的,多进程不共享全局变量,进程之间的内存是相互隔离的;而同一进程下的多个线程,却是共享该进程内的数据;
	4,进程有父子之分,线程没有。一个进程内的所有线程的PID相同。进程与线程的系统资源占比,(100倍差值)。

叄,那么是不是python的多线程就完全没用了呢?

	1、CPU密集型(循环处理、计数等),(多个线程来回切换当然是需要消耗资源),所以多线程对CPU密集型不友好。        
	2、IO密集型(文件处理、网络爬虫等),(单线程下有IO操作会进行IO等待时间浪费,而开启多线程能在线程A等待时,自动切换到线程B,从而能提升程序执行效率)。多线程对IO密集型代码比较友好。
	                            
	请注意:多核多线程比单核多线程更差。单核下多线程,每次释放GIL,无缝执行,但多核下,其他CPU上的线程都会进行竞争,导致其他几个CPU上被唤醒后的线程会醒着等待到切换时间后又进入待调度状态,这样会造成线程颠簸(thrashing),导致效率更低。

肆,总结:python想要充分利用多核CPU,就用多进程。

	原因:每个进程有各自独立的GIL,互不干扰,这样就可以真正意义上的并行执行。  

伍,python2与python3在GIL上的不同?

	1,在Python2.x里,GIL的释放逻辑是当前线程遇见IO操作或者ticks计数达到100(ticks可以看作是Python自身的一个计数器,专门做用于GIL,每次释放后归零,这个计数可以通过 sys.setcheckinterval 来调整),进行释放。        
	而每次释放GIL锁,线程进行锁竞争、切换线程,会消耗资源。并且由于GIL锁存在,python里一个进程永远只能同时执行一个线程(拿到GIL的线程才能执行),这就是为什么在多核CPU上,python的多线程效率并不高。
	
	2,在python3.x中,GIL不使用ticks计数,改为使用计时器(执行时间达到阈值后,当前线程释放GIL),这样对CPU密集型程序更加友好,但依然没有解决GIL导致的同一时间只能执行一个线程的问题,所以效率依然不尽如人意。 

陸,进程与线程的具象类比

进程(process)和线程(thread)是操作系统的基本概念,但是它们比较抽象,不容易掌握。
1.计算机的核心是CPU,它承担了所有的计算任务。假定单个CPU,它是一座工厂,时刻在运行。
2.假定工厂的电力有限,一次只能供给一个车间使用。也就是说,一个车间开工的时候,其他车间都必须停工。背后的含义就是,单个CPU一次只能运行一个任务。
3.进程就好比工厂的车间,它代表CPU所能处理的单个任务。任一时刻,在单核CPU下的多进程或多线程其实都只是并发,不是并行。
4.一个车间里,可以有很多工人。他们协同完成一个任务。
5.线程就好比车间里的工人。一个进程可以包括多个线程。
6.车间的空间是工人们共享的,比如许多房间是每个工人都可以进出的。这象征一个进程的内存空间是共享的,每个线程都可以使用这些共享内存。
7.可是,每间房间的大小不同,有些房间最多只能容纳一个人,比如厕所。里面有人的时候,其他人就不能进去了。这代表一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。比如,多线程资源竞争访问同一个数据的时候的情况。
8.一个防止他人进入的简单方法,就是门口加一把锁。先到的人锁上门,后到的人看到上锁,就在门口排队,等锁打开再进去。这就叫"互斥锁"(Mutual exclusion,缩写 Mutex),防止多个线程同时读写某一块内存区域。某个线程想要执行,必须先拿到GIL,我们可以把GIL看作是“通行证”,并且在一个python进程中,GIL只有一个。拿不到通行证的线程,就不允许进入CPU执行。
9.还有些房间,可以同时容纳n个人,比如厨房。也就是说,如果人数大于n,多出来的人只能在外面等着。这好比某些内存区域,只能供给固定数目的线程使用。
10.这时的解决方法,就是在门口挂n把钥匙。进去的人就取一把钥匙,出来时再把钥匙挂回原处。后到的人发现钥匙架空了,就知道必须在门口排队等着了。这种做法叫做"信号量"(Semaphore),用来保证多个线程不会互相冲突。
当然,当n=1时,mutex是semaphore的一种特殊情况。也就是说,完全可以用后者替代前者。但是,因为mutex较为简单,且效率高,所以在必须保证资源独占的情况下,还是采用这种设计。
11.操作系统的设计,因此可以归结为三点:
(1)以多进程形式,允许多个任务同时运行;
(2)以多线程形式,允许单个任务分成不同的部分运行;
(3)提供协调机制,一方面防止进程之间和线程之间产生冲突,另一方面允许进程之间和线程之间共享资源。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值