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()
View Code
创建线程:
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)
View Code
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())
View Code
线程间的通信:
全局变量进行通信;
线程间的同步和互斥;
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()
View Code
线程 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()
View Code
线程锁:
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()
View Code
创建自己的线程类:
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()
View Code
线程池第三方模块 :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.142379522323608Thread cpu 0.7009162902832031
Thread IO 3.458016872406006Process 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()
View Code
总结:
1,进程和线程的区别
2.会创建使用线程 threading
3.掌握基本的线程间同步互斥编程方法
4.知道什么是GIL
5.了解设计模式的概念
*************************************************************
面试问题:
1. 进程和线程的区别
2. 什么是同步和互斥
3. 给一个具体的情况,问采用进程还是线程为什么
4. 你是怎么处理僵尸进程的
5. 怎么测试一个硬盘的读写速度
6. xxx框架 是用的多进程还是多线程并发
7. 进程间通信方式知道哪些,都有什么特点
同步
互斥