python基础--线程、进程
并发编程:
操作系统:(基于单核研究)
多道技术:
1、空间上的复用
多个程序共用一个计算机
2、时间上的复用
切换+保存状态
例如:洗衣 烧水 做饭
切换:
1、程序遇到IO操作系统会立刻剥夺着CPU的执行权限 IO:input、sleep、accept、recv...阻塞 日常生活中使用的软件通常都是IO密集型
2、当你的程序长时间占用CPU的时候也会被操作系统剥夺CPU的执行权限
进程理论:
进程调度:
时间片轮转法+多级反馈队列
进程三状态:运行、就绪、阻塞
ps:程序不会立刻进入运行状态,都会先在就绪态等待CPU的执行
同步异步:指的是任务的提交方式
同步:提交任务之后原地等待任务的返回结果 期间不做任何事
异步:提交任务之后就立刻执行下一行代码 不等待任务的返回结果 >>> 结果的获取使用异步回调机制
阻塞非阻塞:指的是程序的运行状态
阻塞:阻塞态
非阻塞:就绪态或者运行态
创建进程的两种方式:
使用Process实例化
继承Process类重写run方法
ps:windows在开启进程的时候必须在__main__代码块内,因为windows是以模块导入的方式从上执行代码
什么是进程:正在运行的程序
一个进程对应到内存中就是一块独立的内存空间
join方法:主进程等待某个指定的子进程运行结束,不影响其他子进程的运行
进程对象及其它方法:
current_process().pid
os.getpid
os.getppid
terminate()
is_alive()
守护进程:
daemon:这一句必须放在start之前使用
进程数据是隔离的
互斥锁:
多个进程操作一份数据的时候会出现数据错乱的现象
避免?:将操作数据的部分加锁处理(会将并发变成串行牺牲了效率但是保证了数据的安全)
强锁:acpuire()
释放锁:release()
进程间的通信:
from multiprocessing import Queue q = Queue(5) # 括号内可以传参数 表示的是这个队列的最大存储数 # 往队列中添加数据 q.put(1) q.put(2) # print(q.full()) # 判断队列是否满了 q.put(3) q.put(4) q.put(5) q.put(6) # 当队列满了之后 再放入数据 不会报错 会原地等待 直到队列中有数据被取走(阻塞态) print(q.get()) print(q.get()) print(q.get()) print(q.empty()) # 判断队列中的数据是否取完 print(q.get()) print(q.get()) print(q.empty()) print(q.get_nowait()) # 取值 没有值不等待直接报错 print(q.get()) # 当队列中的数据被取完之后 再次获取 程序会阻塞 直到有人往队列中放入值 """ full get_nowait empty 都不适用于多进程的情况 """
进程间通信IPC机制:
from multiprocessing import Process,Queue def producer(q): q.put('hello GF~') def consumer(q): print(q.get()) if __name__ == '__main__': q = Queue() p = Process(target=producer,args=(q,)) c = Process(target=consumer, args=(q,)) p.start() c.start() """ 子进程放数据 主进程获取数据 两个子进程相互放 取数据 """
生产者消费者模型:
from multiprocessing import Process,Queue,JoinableQueue import random import time def producer(name,food,q): for i in range(10): data = '%s生产了%s%s'%(name,food,i) time.sleep(random.random()) q.put(data) print(data) def consumer(name,q): while True: data = q.get() if data == None:break print('%s吃了%s'%(name,data)) time.sleep(random.random()) q.task_done() # 告诉队列你已经从队列中取出了一个数据 并且处理完毕了 if __name__ == '__main__': q = JoinableQueue() p = Process(target=producer,args=('大厨','馒头',q)) p1 = Process(target=producer,args=('跟班','生蚝',q)) c = Process(target=consumer,args=('william',q)) c1 = Process(target=consumer,args=('john',q)) p.start() p1.start() c.daemon = True c1.daemon = True c.start() c1.start() p.join() p1.join() q.join() # 等到队列中数据全部取出
线程:
进程和线程都是虚拟单位,都是用来帮助我们形象的描述某种事物
进程:资源单位
线程:执行单位
将内存比成加工厂,那么进程就是相当于工厂里面的车间,那么线程就是车间里面的流水线
ps:每个进程都自带一个线程,线程才是真正的执行单位,进程只是在线程运行过程中提供代码运行所需要的资源
为什么要有线程:
开进程:
1、申请内存空间 耗资源
2、“拷贝代码” 耗资源
开线程:
一个进程内可以开多个线程,并且线程与线程之间数据是共享的
ps:开启线程的开销要远远小于启动进程的开销
开启线程的两种方式:
from threading import Thread import time def task(name): print('%s is running'%name) time.sleep(3) print('%s is over'%name) # 开线程不需要在__main__代码块内 但是习惯性的还是写在__main__代码块内 t = Thread(target=task,args=('william',)) t.start() # 告诉操作系统开辟一个线程 线程的开销远远小于进程 # 小的代码执行完 线程就已经开启了 print('主')
from threading import Thread import time class MyThread(Thread): def __init__(self,name): super().__init__() self.name = name def run(self): print('%s is running'%self.name) time.sleep(3) print('%s is over'%self.name) t = MyThread('john') t.start() print('主')
线程对象方法:开启线程不需要在__main__代码块内,但是习惯性的还是写在__main__代码块内
from threading import Thread, current_thread, active_count import time import os def task(name, i): print('%s is running'%name) # print('子current_thread:',current_thread().name) # print('子', os.getpid()) time.sleep(i) print('%s is over'%name) # 开线程不需要在__main__代码块内 但是习惯性的还是写在__main__代码块内 t = Thread(target=task, args=('john',1)) t1 = Thread(target=task, args=('william',2)) t.start() # 告诉操作系统开辟一个线程 线程的开销远远小于进程 t1.start() # 告诉操作系统开辟一个线程 线程的开销远远小于进程 t1.join() # 主线程等待子线程运行完毕 print('当前正在活跃的线程数',active_count()) # 小的代码执行完 线程就已经开启了 print('主') # print('主current_thread:',current_thread().name) # print('主', os.getpid())
守护线程:主线程的结束也就意味着进程的结束,主线程必须等待其它非守护线程的结束才能结束(意味着子线程在运行的时候需要使用进程中的资源,而主线程一旦结束了资源也就销毁了)
from threading import Thread,current_thread import time def task(i): print(current_thread().name) time.sleep(i) print('GG') # for i in range(3): # t = Thread(target=task,args=(i,)) # t.start() t = Thread(target=task,args=(1,)) t.daemon = True t.start() print('主')
线程间相互通信:
from threading import Thread money = 666 def task(): global money money = 999 t = Thread(target=task) t.start() t.join() print(money) # money变成了999
线程中的互斥锁:
from threading import Thread,Lock import time n = 100 def task(mutex): global n mutex.acquire() tmp = n time.sleep(0.1) n = tmp - 1 mutex.release() t_list = [] mutex = Lock() for i in range(100): t = Thread(target=task,args=(mutex,)) t.start() t_list.append(t) for t in t_list: t.join() print(n)
线程中的小例子:
from threading import Thread from multiprocessing import Process import time def foo(): print(123) time.sleep(1) print("end123") def bar(): print(456) time.sleep(3) print("end456") if __name__ == '__main__': t1=Thread(target=foo) t2=Thread(target=bar) t1.daemon=True t1.start() t2.start() print("main-------")
这里需要注意的是:主线程要等待非守护线程的结束才会结束所以在这里面end123也是会输出的