线程理论,进程池,线程池,协程
多进程实现TCP服务端并发
import os
import socket
from multiprocessing import Process
def get_server():
server = socket.socket()
server.bind(('127.0.0.1', 14334))
server.listen(5)
return server
def get_talk(sock):
while True:
date = sock.recv(1024)
print(date.decode('utf8'))
sock.send(date.upper())
if __name__ == '__main__':
server = get_server()
while True:
sock,addr = server.accept()
p = Process(target=get_talk,args = (sock,))
p.start()
客户端:
import socket
client = socket.socket()
client.connect(('127.0.0.1', 14334))
while True:
client.send('你好啊'.encode('utf8'))
data = client.recv(1024)
print(data.decode('utf8'))
线程理论
线程: 执行单位
进程: 资源单位
进程相当于 单个车间,线程相当于车间里的流水线
一个进程内部至少都含有一个线程。
同一个进程可以开设多个线程,同一个进程下的线程数据是共享的。
创建线程的两种方式
第一种:
使用 threading 模块导入 Thread
import time
from threading import Thread
def task(name):
print(f'{name} is running')
time.sleep(3)
print(f'{name} is over')
t = Thread(target=task,args = ('tank',))
t.start()
print('主线程')
第二种重写init方法:
import time
from threading import Thread
class MyThread(Thread):
def __init__(self, name):
super().__init__()
self.name = name
def run(self):
print(f'{self.name} is running')
time.sleep(1)
print(f'{self.name} is over')
obj = MyThread('tank')
obj.start()
print('主线程')
线程的诸多方法
join方法
# join方法就是主线程代码等待子线程代码运行结束之后再往下执行
from threading import Thread
import time
def task(name):
print(f'{name} is runing')
time.sleep(3)
print(f'{name} is over')
t = Thread(target=task, args=('owen', ))
t.start()
t.join()
print('主线程')
"""
主线程为啥要等到子线程结束才结束整个过程
因为要确保子线程运行过程中需要的各项资源正常供给,主线程才能放心结束
"""
同一个进程内的多个线程数据共享
from threading import Thread
money = 9999999
def task():
global money
money = 9
t = Thread(target=task)
t.start()
t.join()
print(money)
线程对象属性和方法
current_thread() # 获取线程的名字
active_count() # 统计进程下活跃的线程
1、验证一个进程下的多个线程是否真的处于一个进程
from threading import Thread
import os
import time
def task():
print('子线程进程号', os.getpid())
t = Thread(target=task)
t.start()
time.sleep(3)
print('主线程进程号', os.getpid())
# 子线程进程号 20424
# 主线程进程号 20424
进程号相同, 所以是一个进程
====================================================================================
2、统计进程下活跃的线程
import threading
print(threading.active_count())
====================================================================================
3、获取线程的名字
from threading import Thread
class MyThread(Thread):
def run(self):
print(self.name)
t1 = MyThread()
t2 = MyThread()
t1.start()
t2.start()
# Thread-1 子线程
# Thread-2 子线程
class MyThread(Thread):
def run(self):
print(current_thread().name)
t1 = MyThread()
t2 = MyThread()
t1.start()
t2.start()
# <MyThread(Thread-1, started 51400)>
# <MyThread(Thread-2, started 28464)>
GIL全局解释器锁
# 官方文档
In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly
because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)
"""
python解释器的类别有很多
Cpython Jpython Ppython
GIL只存在于CPython解释器中,不是python的特征
GIL是一把互斥锁用于阻止同一个进程下的多个线程同时执行
原因是因为CPython解释器中的垃圾回收机制不是线程安全的
垃圾回收机制
引用计数、标记清除、分代回收
==================================================================================
反向验证GIL的存在 如果不存在会产生垃圾回收机制与正常线程之间数据错乱
GIL是加在CPython解释器上面的互斥锁
同一个进程下的多个线程要想执行必须先抢GIL锁 所以同一个进程下多个线程肯定不能同时运行 即无法利用多核优势
强调:同一个进程下的多个线程不能同时执行即不能利用多核优势
python是垃圾 速度太慢 有多核都不能用
反怼:虽然用一个进程下的多个线程不能利用多核优势 但是还可以开设多进程!!!
再次强调:python的多线程就是垃圾!!!
反怼:要结合实际情况
如果多个任务都是IO密集型的 那么多线程更有优势(消耗的资源更少)
多道技术:切换+保存状态
如果多个任务都是计算密集型 那么多线程确实没有优势 但是可以用多进程
CPU越多越好
以后用python就可以多进程下面开设多线程从而达到效率最大化
"""
1.所有的解释型语言都无法做到同一个进程下多个线程利用多核优势
2.GIL在实际编程中其实不用考虑
GIL与普通互斥锁
GIL只能够确保同进程内多线程数据不会被垃圾回收机制弄乱
并不能确保程序里面的数据是否安全
所以我们在平时编写代码的时候如果需要进行加锁处理还是要自己手动加锁
# 判断GIL的存在
from threading import Thread
money = 100
def task():
global money
money -= 1
for i in range(100):
t = Thread(target=task)
t.start()
print(money)
# 0
# 通过验证不同数据加不同锁
from threading import Thread, Lock
import time
money = 100
mutex = Lock()
def task():
global money
mutex.acquire() # 抢锁
tmp = money
time.sleep(0.2)
money = tmp - 1
mutex.release() # 解锁
t_list = []
for i in range(100):
t = Thread(target=task)
t.start()
t_list.append(t)
for t in t_list:
t.join()
print(money) # 等待线程运行完毕再打印
python多线程是否有用
"""
前提:
CPU的个数:单个、多个
任务的类型:IO密集型、计算密集型
"""
# 单个CPU时
多个IO密集型任务
多线程: 浪费资源,无法利用多个CPU
多线程: 节省资源,切换 + 并保存状态
多个计算密集型任务
多进程: 耗时更长, 创建进程的消耗 + 切换消耗
多线程: 耗时较短, 切换消耗
# 多个CPU时
多个IO密集型任务
多进程: 浪费资源, 多个CPU无用武之地
多线程: 节省资源, 切换 + 保存状态
多个计算密集型任务
多进程: 利用多核, 速度快
多线程: 速度较慢
结论: 多进程和多线程都有具体的应用场景, 尤其是多线程并不是没有用
----------------------------------------------------------------------------------- 计算密集型:
# 多进程
from multiprocessing import Process
import time
def work():
res = 1
for i in range(1, 10000):
res *= i
if __name__ == '__main__':
# print(os.cpu_count()) # 8 可以获取计算机的CPU个数
start_time = time.time()
p_list = []
for i in range(12):
p = Process(target=work)
p.start()
p_list.append(p)
for p in p_list:
p.join()
print('总耗时:%s' % (time.time() - start_time))
总耗时:0.18518519401550293
# 多线程
from threading import Thread
from multiprocessing import Process
import os
import time
def work():
res = 1
for i in range(1, 10000):
res *= i
if __name__ == '__main__':
# print(os.cpu_count()) # 8 可以获取计算机的CPU个数
start_time = time.time()
t_list = []
# 多线程操作
for i in range(12):
t = Thread(target=work)
t.start()
t_list.append(t)
for t in t_list:
t.join()
print('总耗时:%s' % (time.time() - start_time))
总耗时: 0.20554852485656738
"""
计算密集型
多进程
总耗时:0.18518519401550293
多线程
总耗时:0.20554852485656738
所以多进程好
"""
===================================================================================
IO密集型:
from threading import Thread
from multiprocessing import Process
import os
import time
def work():
time.sleep(2)
if __name__ == '__main__':
start_time = time.time()
t_list = []
for i in range(100):
t = Thread(target=work)
t.start()
for t in t_list:
t.join()
print('总耗时:%s' % (time.time() - start_time))
# 总耗时:0.009966373443603516
if __name__ == '__main__':
start_time = time.time()
p_list = []
for i in range(100):
p = Process(target=work)
p.start()
for p in p_list:
p.join()
print('总耗时:%s' % (time.time() - start_time))
# 总耗时: 0.9350748062133789
"""
IO密集型
多进程
总耗时: 0.9350748062133789
多线程
总耗时:0.009966373443603516
所以多线程好
"""
-
死锁现象
from threading import Thread, Lock
import time
class MyThread(Thread):
def run(self):
self.func1()
self.func2()
def func1(self):
mutexA.acquire()
print(f'{self.name}抢到了A锁')
mutexB.acquire()
print(f'{self.name}抢到了B锁')
mutexB.release()
print(f'{self.name}释放了B锁')
mutexA.release()
print(f'{self.name}释放了A锁')
def func2(self):
mutexB.acquire()
print(f'{self.name}抢到了B锁')
time.sleep(1)
mutexA.acquire()
print(f'{self.name}抢到了A锁')
mutexA.release()
print(f'{self.name}释放了A锁')
mutexB.release()
print(f'{self.name}释放了B锁')
for i in range(10):
obj = MyThread()
obj.start()
# 出现死锁现象
出现了一个线程抢到了A锁,另一个线程抢到了B锁,两个互不相让导致卡死
信号量
# 信号量在不同的体系里面, 展现出来的功能是不一样
在并发编程中信号量意思就是多把互斥锁
"""
将自定义互斥锁比喻成豪华个人卫生间
信号量就相当于公共厕所,里面有多个位子
"""
sp.acquire() 抢锁
sp.release() 放锁
--------------------------------------------
from threading import Thread, Semaphore
import time
import random
sp = Semaphore(5) # 自定义五个带门锁的坑位的厕所
def task(name):
sp.acquire() # 抢锁
print('%s正在蹲坑' % name)
time.sleep(random.randint(1, 5))
sp.release() # 放锁
for i in range(1, 21):
t = Thread(target=task, args=('小赤佬%s号'% i, ))
t.start()
event事件
event.set() # 使下面含有event.wait()代码的函数继续运行
event.wait() # 没接收到event.set()指令前,一直等待。
============================================
from threading import Thread, Event
import time
event = Event() # 创建一个类似红路灯对象
def light():
print('红灯停')
time.sleep(3)
print('绿灯行')
event.set()
def car(name):
print('%s正在等待红灯'% name)
event.wait()
print('%s丧心病狂的疯跑'% name)
t = Thread(target=light)
t.start()
for i in range(20):
t = Thread(target=car, args=('伞兵%s'%i, ))
t.start()
# 结果显示,前面先在等红灯, 然后时间一到就开始跑了
进程池与线程池
因为硬件的发展跟不上软件的发展,硬件的自身性能有限,我们在编写代码的时候如果无限制 建立进程与线程会导致计算机死机奔溃。
因此 进程与线程并不能无限制创建
1.进程池:
提前创建好固定数量的进程 供调用 超出等待
2.线程池:
提前创建好固定数量的线程 供调用 超出等待
代码实操:
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
import os
import time
import random
from threading import current_thread
# 产生含有固定数量的线程 的线程池
pool = ProcessPoolExecutor(10)
def task(n):
print('开始')
return '我是task函数的返回值'
def func(*args,**kwargs):
print('反馈机制')
if __name__ == '__main__':
# 提交任务给线程池
for i in range(20):
res = pool.submit(task, 123) # 朝线程池提交任务
pool.submit(task, 123).add_done_callback(func)
协程
"""
进程:资源单位
线程:执行单位
协程:单线程下实现并发(效率极高)
并发的概念: 切换 + 保存状态
对于操作系统来认识进程和线程
协程就是自己通过代码来检测程序的IO操作并自己处理 让CPU感觉不到IO的存在, 从而最大幅度的占用CPU
"""
# 固定编写, 用于检测所有的IO操作
from gevent import monkey; monkey.patch_all()
from gevent import spawn
import time
def play(name):
print('%s 在玩娃娃'% name)
time.sleep(5)
print('%s 在玩皮球'% name)
def eat(name):
print('%s 在吃龙虾' % name)
time.sleep(3)
print('%s 在吃螺蛳粉' % name)
start_time = time.time()
g1 = spawn(play, 'owen')
g2 = spawn(eat, 'owen')
g1.join()
g2.join()
print('总耗时:', time.time() - start_time) # 正常串行是8s+
# 结果为
owen 在玩娃娃
owen 在吃龙虾
owen 在吃螺蛳粉
owen 在玩皮球
总耗时: 5.03695273399353 # 代码控制切换
协程实现并发
# 服务端
from gevent import monkey;monkey.patch_all()
from gevent import spawn
import socket
def communication(sock):
while True:
data = sock.recv(1024) # IO操作
print(data.decode('utf8'))
sock.send(data.upper())
def get_server():
server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(5)
while True:
sock, addr = server.accept() # IO操作
spawn(communication, sock)
g1 = spawn(get_server)
g1.join()
# 客户端
from threading import Thread, current_thread
import socket
def get_client():
client = socket.socket()
client.connect(('127.0.0.1', 8888))
count = 0
while True:
msg = '%s 芜湖 %s'%(current_thread().name, count)
count += 1
client.send(msg.encode('utf8'))
data = client.recv(1024)
print(data.decode('utf8'))
# 创建多线程
for i in range(200):
t = Thread(target=get_client())
t.start()
"""
python可以通过开设多进程, 在多进程开设多线程在多线程使用协程
实际很少需要那么高的效率
"""