@多任务的概念
操作系统同时可以运行多个任务
1,单核cpu实现多任务原理操作系统让各任务在cpu中轮流执行,因为速度太快而被认为各任务是同时实现的
- 并发:看上去一起执行,任务数多于cpu核心数
- 并行:真正一起执行,任务数小于等于cpu核心数
@实现任务的方式
1,多进程方式
2,多线程方式
3,协程模式
4,多进程+多线程
@进程的概念
对于操作系统来说,一个任务就是一个进程。
进程是系统中程序执行和资源分配的基本单位,每个进程都有自己的数据段,代码段,和堆栈段。
@进程的实现
常用的是multprocessingku,它是跨平台版本的多进程模块,提供了一个Process类来代表一个进程对象
#导如Process库
from multprocessing import Process
#创建子进程,target说明进程执行的任务
p = Process(target=fnc,arg=(,))
#启动进程
p.start()
#标识等待子进程执行完主进程才能执行完
p.join()
ps: os的库内方法可以获取进程ip
import os
#获取当前进程的id号
pip = os.getpid()
#获取当前进程的主进程ip
pipp = os.getppid()
@进程执行过程的先后顺序
各进程执行是没有顺序的,我们可以在每个进程后面加上join方法让子进程完成后主进程才能结束
父进程的执行完成不会影响子进程,但常规下主进程是不执行主要任务的,它的功能是分配任务给子进程去干,所有总逻辑上来看,主进程应该是最后结束比较好
@进程的全局变量
进程的全局比变量是不共享的,当子进程需要用到全局变量时,会拷贝一个全局变量使用当不会影响到全局变量
@进程的进程池POOL
#导入Pool库
from multprocessing import Pool
#创建多进程池
p = Pool(x)//x是自定义的cpu核心数,默认是本机的核心数,表示可以同时执行多少个进程
#创建进程,放入进程池表示同意管理
pp.apply_async(fnc,arg=(,))
#在调用join之前必须先调用close,调用close之后就不能再继续添加新的进程了
pp.close()
#进程池对象调用join方法,会等待进程池内所有的子进程结束后再去执行父进程
pp.join()
ps:当任务量较少时使用进程时,效率可能比单进程的还慢,因为创建进程很好是,耗资源的,进程数多的话,cpu会大部分用于切换进程,而用于执行的反而少了
@强制结束进程
当进程是个死循环是,无法等待其结束,只能强制结束
使用pr.terminate()
@线程的概念
在一个进程的内部,要同时干多件事,就需要同时运行多个“子任务”,我们把进程内的这些“子任务”叫做线程
线程通常叫做轻型的进程。线程是共享内存空间的并发执行的多任务,没一个线程都共享一个进程的资源。
线程是最小的执行单元,而进程有至少一个线程组成,如何调度进程和线程完全由操作系统决定,程序自己不能决定什么时候执行,执行多长时间
- 模块
1,_thread模块 低级模块(比较接近底层)
2,threading模块 高级模块(对_thread模块的封装)
@线程的实现
任何进程默认救护启动一个主线程,主线可以启动新的子进程
#导入threading库
import threading
#current_thread()返回当前线程的实例
threading.current_thread().name//查看当前进程的name
#创建子线程
t = threading.Thread(target=fnc,arg=(,))
#启动线程
t = start()
#等待线程结束
t = join()
@多线程的数据共享
多线程中,所有变量都由所有线程共享。所有,任何一个变量都可以被任意线程修改,因此,线程之间共享数据的最大危险在于多个线程同时修改一个变量,容易把数据混乱。
@线程锁解决数据混乱
对线程执行的数据运算进行枷锁 ,加了锁的运算必须等一个线程执行完才能由其它线程使用,这样多线程变成单线程执行效率降低
由于可以存在多个锁,不同线程持有不同的锁,并试图获取其它锁,了能造成死锁,导致多个线程挂起,只能靠操作系统强制终止。
第一种锁
#创建一个锁对象
lock = threading.Lock()
for i in range(100000):
#进行枷锁
lock.acquire()
try://为保险锁完后完解锁,或是出错完成不了解锁使用try
num = num +n
num = num -n
finally:
#修改完一定要释放锁
lock.release()
第二种锁
#实现的功能和第一种方法一样,不过with lock可以自动上锁与解锁,减少死锁几率
with lock:
num = num +n
num = num -n
@完美解决线程的数据共享
使用ThreadLoocal对象
#创建一个全局的ThreadLoocal对象,每个线程有独立的存储空间,每个线程对ThreadLoocal对象都可以读写,但互不影响
local = thread.Loocal()
#把全局变量赋值给local
local.x = num //给线程增加x属性
#当线程需要调用全局变量时,把loca.x当成num调用,local就是线程的局部变量
@自定义控制线程数量
对于一个进程,我们可以规定使用多少个线程去执行
使用sem = threading.Semaphore(x)
x是自定义的线程数
在调用线程的方法内增加with sem
def fnc():
with sem:
@存够一定数量再执行
等待线程数量达到规定数量时再执行wait()后面的
使用bar= threading.Barrier(x)
x是自定义等待的线程数
在调用线程的方法内增加bar.wait()
def fnc():
print(1)
bar.wait()
print(2)
#线程会全部执行print(1)然后等待线程数达到x个才去执行print(2)
@定时线程
线程在指定时间后执行单位为秒
使用t = threading.Timer(5,fnc)
//fnc在5秒后执行
@线程通信
卡住不让程序往下执行,必须等到事件信号后才能执行
def fnc():
#事件对象
event = threading.Event()
def run():
for i in range(5):
#等待事件触发信号
event.wait()
#重置信号,不重置的话下次就不卡了,等同于正常运行,不需要再等待触发事件
event.clear()
print("sunck is a good man !!%d"%i)
t = threading.Thread(target=run).start()
return event
e = fnc()
#触发事件
e.set()//每次执行一次触发一次
@线程调度
安排不同线程以指定的秩序执行
使用线程条件变量cond = threading.Condition()
控制
def run1():
with cond:
for i in range (0,10,2):
print(threading.current_thread().name,i)
time.sleep()
cond.wati()// 第一步,上面执行完,进入休眠
cond.notify()//第四步,本部分第二次执行完,给自己打标签表明我执行过了
def run2():
with cond:
for i in range (1,10,2):
print(threading.current_thread().name,i)
time.sleep()
cond.notify()// 第二步,给自己打标签表明,表明自己执行过了
cond.wait()//第三部,进入休眠
一个简单的线程调度就是这样一个过程
@进程和线程的比较
@多任务的实现原理
通常我们会设计Master-Worker模式,Master负责分配任务,Worker负责执行任务,因此,多任务环境下,通常hi一个Master,多个Worker
@多进程
1,主进程就是Master,其它进程就是Worker
2,优点
- 稳定性高
一份子进程崩溃了,不会影响主进程和其它子进程,如果主进程挂了,所有进程都得挂。
3,缺点
- 创建进程代价大
在Unix/Linux系统下,用fork调用还行,在Windows下创建进程开销巨大
- 操作系统能同时运行进程数量有限
在内存和cpu限制下,如果有几千个进程同时运行,操作系统连调度都成问题
@多线程
1,主线程就是Master,其它线程就是Worker
2,优点 - 多线程模式通常比多进程块一点,但也块不到哪去
在Windows下,多线程的效率比多进程更高
3,缺点任何一个线程挂掉都可能直接造成整个进程崩溃
@协程
协程只有一个线程,在不引用或调用的情况下,完成同样的效果,因为只有一个线程,所有不存在变量冲突,自己只用一个资源,效率极高
def run():
print(1)
yield 10
print(2)
yield 20
print(3)
yield 30
#协程的最简单风格,控制函数的阶段执行,节约线程或者进程的切换
m = rum()//返回值是一个生成器
print(next(m))// 1 10 一个线程
print(next(m))// 2 20 一个线程
print(next(m))// 3 30 一个线程
@数据的传输
def run():
#空变量,存储的作用data始终为空
def run():
data =''
r = yield data
#r=a
print(1,r,data)
r = yield data
#r =b
print(2,r,data)
r = yield data
#r =c
print(3,r,data)
#启动m
m = run()
print(m.send(None))
print(m.send('a'))
print(m.send('b'))
print(m.send('c'))
@协程的数据传输实例
def product():
c.send(None)
for i in range(5):
print("成产者产生数据%d"%i)
r = c.send(str(i))
print("消费者消费了数据%s"%r)
c.close()
def customer():
data=""
while True:
n =yield data
if not n:
return
print("消费者消费了%s"%n)
data = "200"
c = customer()//产生消费者
product(c)
输出
成产者产生数据0
消费者消费了0
消费者消费了数据200
成产者产生数据1
消费者消费了1
消费者消费了数据200
成产者产生数据2
消费者消费了2
消费者消费了数据200
成产者产生数据3
消费者消费了3
消费者消费了数据200
成产者产生数据4
消费者消费了4
消费者消费了数据200
过程: