GIL与普通互斥锁区别、多线程、死锁、信号量、进程池和线程池、协程

  • GIL与普通互斥锁区别

  • 验证多线程是否有用

  • 死锁现象

  • 信号量与event事件

  • 进程池与线程池

  • 协程

  • 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)  # 等待线程运行完毕再打印
# 0

"""
  GIL是一个纯理论知识, 在实际工作中不同考虑它的存在,
  GIL作用面很窄, 仅限于解释器级别
"""
  • 验证多线程作用

"""
 前提:
    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(8):
        p = Process(target=work)
        p.start()
        p_list.append(p)
    for p in p_list:
        p.join()
    print('总耗时:%s' % (time.time() - start_time))
    总耗时:0.19758033752441406
        
# 多线程       
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()
    p_list = []
    for i in range(8):
        p = Process(target=work)
        p.start()
        p_list.append(p)
    for p in p_list:
        p.join()
    t_list = []
    for i in range(8):
        t = Thread(target=work)
        t.start()
        t_list.append(t)
    for t in t_list:
        t.join()
    print('总耗时:%s' % (time.time() - start_time))  
    总耗时:0.322171688079834
 """
   计算密集型
     多进程
       0.19758033752441406
     多线程
        0.322171688079834
   所以多进程好
 """
  • IO密集型

# 多线程
from threading import Thread
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.011942148208618164

# 多进程
def work():
    time.sleep(2)

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.5508136749267578
"""
 IO密集型
   多线程
     0.011942148208618164
   多进程
     0.5508136749267578
  结论:
    多线程更好
"""
  • 死锁现象

from threading import Thread, Lock
import time

mutexA = Lock()
mutexB = Lock()

class MyThread(Thread):
    def run(self):
        self.f1()
        self.f2()
    def f1(self):
        mutexA.acquire()
        print(f'{self.name} 得到了A锁')
        mutexB.acquire()
        print(f'{self.name} 得到了B锁')
        mutexB.release()
        mutexA.release()
    def f2(self):
        mutexB.acquire()
        print(f'{self.name} 得到了B锁')
        time.sleep(2)
        mutexA.acquire()
        print(f'{self.name} 得到了A锁')
        mutexA.release()
        mutexB.release()
for i in range(20):
    t = MyThread()
    t.start()
  # 出现死锁现象
"""
锁不能轻易使用并且我们也不会自己去处理锁,因为都是拿别人封装的工具
"""

LctWiF.jpg

  • 信号量

# 信号量在不同的体系里面, 展现出来的功能是不一样
在并发编程中信号量意思就是多把互斥锁
在django框架中信号量意思就是达到某个条件自动触发特定的功能
"""
 将自定义互斥锁比喻成豪华个人卫生间
 信号量就相当于公共厕所,里面有多个位子
"""
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()

LcaOiT.jpg

  • event事件
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()
 # 结果显示,前面先在等红灯, 然后时间一到就开始跑了

LcBBkt.jpg

  • 进程池与线程池

"""
  服务端必备的三要素
      1、24小时不间断提供服务
      2、固定的IP和port
      3、支持高并发
"""
TCP服务端实现并发
  多进程: 来一个人客户端开一个进程
  多线程: 来一个客户端开一个线程
    
计算机硬件是有物理极限的,我们不可能无限制的创建进程和线程
解决方法:
   """ 池:
       保证计算机硬件安全的情况下提升程序的运行效率
    进程池:
        提前创建好固定数量的进程, 后续反复使用这些进程
    线程池:
        提前创建好固定数量的线程,后面反复使用线程
        
 当任务超出了池子里面的最大进程或线程数, 就原地等待
 进程池和线程池其实降低了程序的运行效率, 但是它保证了硬件的安全
    """
# 代码
# 线程池
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
import time
from threading import current_thread
def task(n):
    time.sleep(3)
    print(n)
    print(current_thread().name)  # 线程最大数为4
def func(*args, **kwargs):
    print(args, kwargs)

pool = ThreadPoolExecutor(5)  # 线程池线程数默认是CPU的五倍,也可以自定义
'''让异步提交自动提醒>>>:异步回调机制'''
for i in range(10):
    pool.submit(task, i).add_done_callback(func)
"""
 add_done_callback当任务有结果的时候, 自动调用括号内的函数处理
"""

# 进程池
pool = ProcessPoolExecutor(5)  # 进程池进程数默认是CPU个数 也可以自定义
'''代码执行之后就会立刻创建五个等待工作的进程'''
pool.submit(task, i).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  # 代码控制切换
  • 基于协程实现TCP服务端并发

# 服务端
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可以通过开设多进程, 在多进程开设多线程在多线程使用协程
 实际很少需要那么高的效率
"""

LcjpP1.jpg

posted @ 2022-04-21 22:36  未月  阅读(52)  评论(0)    收藏  举报