Python之路PythonThread,第四篇,进程4
python3 进程/线程4
进程间同步互斥方法:
from multiprocessing import Lock
创建 进程锁对象
lock = Lock()
lock.acquire() 给临界区上锁
lock.release() 给临界区解锁
说明:1,具体实现上 acquire() 为一个条件阻塞函数;
2,当有任意一个进程先进行了acquire操作后,其他进程再企图进行acquire操作时就会阻塞,直到lock对象被release后其他进程CIA可以进行下次
acquire操作;
with lock: 也可以实现加锁,解锁
线程:
1,线程也可以使用计算机的多核资源,也是多任务编程方式之一;
2, 线程又称为轻量级的进程,在并发上和进程相同,但是在创建时销毁资源少;
说明:1,一个进程中可以包含多个线程,这个多个线程共享进程的资源;
2, 多个线程因为共享进程的资源,所以在通信上往往采用全局变量的方式;
3,线程也有自己特有的资源,比如TTD指令集等;
多进程和多线程的区别和联系:
1,多进程和多线程都是多任务编程方式,都可以使用计算机多核;
2,进程的创建要比线程消耗更多的资源;
3,进程的空间独立数据更安全,有专门的进程间通信方式进行交互;
4,一个进程包含多个线程,所以线程共享进程资源,没有专门的通信方式,依赖全局变量进行通信。 往往需要使用同步互斥机制,逻辑需要考虑更多;
5,进程线程都有自己特有的资源,多个关联任务的时候使用多线程资源消耗更少; 如果是多个无关联任务也不适用全部都使用线程;
1 from multiprocessing import Process,Lock 2 import time,sys 3 4 def worker1(stream): 5 lock.acquire() # 加锁 6 for i in range(5): 7 time.sleep(1) 8 stream.write("Lock acquired via\n") 9 lock.release()#解锁 10 11 def worker2(stream): 12 # lock.acquire() 13 with lock: #加锁 语句块结束即解锁 14 for i in range(5): 15 time.sleep(1) 16 stream.write("Lock acquired directly\n") 17 # lock.release() 18 19 lock = Lock() 20 #sys.stdout为所有进程都拥有的资源 21 w1 = Process(target=worker1,args=(sys.stdout,)) 22 w2 = Process(target=worker2,args=(sys.stdout,)) 23 24 w1.start() 25 w2.start() 26 27 w1.join() 28 w2.join()
创建线程:
import threading
创建线程函数
threading.Tread()
功能:创建线程
参数:target 线程函数
args 以元组方式给线程函数传参
kwargs 以字典方式给线程函数传参
name 线程名称(默认Thread-1)
返回值:返回线程对象
线程属性和方法:
t.start() 启动一个线程
t.is_alive() 查看一个线程的状态
t.name 查看线程的名称
t.join([esc]) 阻塞等待回收线程
1 import threading 2 from time import ctime,sleep 3 4 a = 10 5 6 def music(sec): 7 print("Listening music") 8 global a 9 a = 1000 10 sleep(sec) 11 12 t = threading.Thread(name = "my thread",\ 13 target = music,args = (2,)) 14 15 t.start() 16 print("创建线程") 17 sleep(3) 18 print(a)
deamon属性:
1,设置该属性默认为False, 主线程执行完毕后不会影响其他线程的执行;
2, 如果设置为True, 则主线程执行完毕其他线程也终止执行;
代码:
设置daemon: t.setDaemon(True) 或 t.daemon = True
获取daemon属性值: t.isDaemon()
import threading from time import sleep,ctime def fun(): print("This is a thread test") sleep(5) print("thread over") t = threading.Thread(name = 'levi',\ target = fun) # t.setDaemon(True) t.daemon = True print(t.isDaemon()) t.start() print(t.is_alive()) #线程状态 print(t.name) #线程名称 t.join(2) print("all over",ctime())
线程间的通信:
全局变量进行通信;
线程间的同步和互斥;
import threading from time import sleep s = None e = threading.Event() def bar(): print("呼叫foo") global s s = "天王盖地虎" def foo(): print('foo等口令') sleep(2) print('foo收到 %s'%s) e.set() def fun(): sleep(1) e.wait() print("内奸出现") global s s = "小鸡炖蘑菇" t1 = threading.Thread\ (name = 'bar',target = bar) t2 = threading.Thread\ (name = 'foo',target = foo) t3 = threading.Thread\ (name = 'fun',target = fun) t1.start() t2.start() t3.start() t1.join() t2.join() t3.join()
线程 event:
创建事件对象: e = threading.Event()
e.wait([timeout]) 如果e被设置则不会阻塞,未被设置则阻塞 ;timeout为阻塞的超时时间;
e.set() 将e变为设置是状态
e.clear() 将e变为未设置状态;
from threading import * import random from time import sleep a = 500 #创建事件对象 e = Event() #子线程不断减少a 但是希望a的值不会少于100 def fun(): global a while True: sleep(2) print('a = ',a) e.wait() a -= random.randint(0,100) t = Thread(target = fun) t.start() #主线程不断的让a增加以确保a不会小于100 while True: sleep(1) a += random.randint(1,10) if a > 100: e.set() else: e.clear() t.join()
线程锁:
lock = threading.Lock() 创建线程锁;
lock.acquire() 上锁
lock.release() 解锁
import threading a = b = 0 lock = threading.Lock() def value(): while True: lock.acquire() if a != b: print("a = %d,b = %d"%(a,b)) lock.release() t = threading.Thread(target = value) t.start() while True: lock.acquire() a += 1 b += 1 lock.release() t.join()
创建自己的线程类:
1,自定义类 继承于 原有线程类Thread
2, 复写原有的run方法;
3,创建线程对象调用start的时候会自动执行run
from time import ctime,sleep import threading #编写自己的线程类 class MyThread(threading.Thread): def __init__(self,func,args,name = 'Levi'): threading.Thread.__init__(self) self.func = func self.name = name self.args = args #自定义 线程启动函数 def run(self): self.func(*self.args) #待启动的线程函数 def player(file,time): for i in range(2): print('start playing %s:%s'\ %(file,ctime())) sleep(time) t = MyThread(player,('baby.mp3',3)) t.start() t.join()
线程池第三方模块 :threadpool
sudo pip3 install threadpool
GIL (全局解释器锁)
python ---> 支持多线程 ----> 同步和互斥 ---> 加锁 --->超级锁 ---> 解释器在同一时刻只能解释一个线程;
大量python库为了省事依赖于这种机制----> python多线程效率低;
GIL 即为从python解释器由于 上锁带了的同一时刻只能解释一个线程的问题;
解决方案:
1, 不使用线程,转而使用进程;
2, 不使用c 作为解释器 java c# 都可以做python解释器;
(1)IO密集型: 程序中进行了大量的IO操作,只有少量的CPU操作;
在内存中进行了数据的交换的操作都可以认为是IO操作;
特点: 速度较慢,使用cpu不高;
(2)cpu密集型(计算密集型):大量的程序都在进行运算操作;
特点:cpu占有率高
效率测试:
Line cpu 1.224205732345581
Line IO 4.142379522323608
Thread cpu 0.7009162902832031
Thread IO 3.458016872406006
Process cpu 0.6419346332550049
Process IO 1.8482108116149902
总结:多线程的工作效率和单线程几乎相近,而多进程要比前两者有明显的效率提升
设计模式:
设计模式代表了一种最佳实践,是被开发人员长期开发总结,用来解决某一类问题的思路方法。这种方法保证了代码的效率,也易于理解;
单例模式,工厂模式,生产者模式。。。
生产者消费者模式:
高内聚: 在同一模块内,实现单一功能,尽量不使功能混杂;
低耦合: 不同的模块之间尽量相互独立,减少模块间的影响;
代码实现
from threading import Thread #python标准库中的队列模块 import queue import time #创建一个队列模型作为商品的仓库 q = queue.Queue() class Producer(Thread): def run(self): count = 0 while True: if q.qsize() < 50: for i in range(3): count += 1 msg = "产品 %d"%count q.put(msg) #将产品放入队列 time.sleep(1) class Customer(Thread): def run(self): while True: if q.qsize() > 20: for i in range(2): msg = q.get() #从仓库拿到商品 print("消费了一个 %s"%msg) time.sleep(1) #创建三个生产者 for i in range(3): p = Producer() p.start() #创建5个消费者 for i in range(5): p = Customer() p.start()
总结:
1,进程和线程的区别
2.会创建使用线程 threading
3.掌握基本的线程间同步互斥编程方法
4.知道什么是GIL
5.了解设计模式的概念
*************************************************************
面试问题:
1. 进程和线程的区别
2. 什么是同步和互斥
3. 给一个具体的情况,问采用进程还是线程为什么
4. 你是怎么处理僵尸进程的
5. 怎么测试一个硬盘的读写速度
6. xxx框架 是用的多进程还是多线程并发
7. 进程间通信方式知道哪些,都有什么特点
同步
互斥