GIL锁,多线程作业,死锁,信号,event事件,进程池与线程池,协程

1. GIL与普通互斥锁区别

# 1.先验证GIL的存在
  from threading import Thread, Lock
  import time
  money = 100
  def task():
      global money
      money -= 1
  for i in range(100):  # 创建一百个线程
      t = Thread(target=task)
      t.start()
  print(money)

 

 

 

# 2.再验证不同数据加不同锁
from threading import Thread, Lock
import time

money = 100
mutex = Lock()


def task():
    global money
    tmp = money
    time.sleep(0.1)
    money = tmp - 1


thread_list = []
for i in range(100):  # 创建一百个线程
    t = Thread(target=task)
    t.start()
    thread_list.append(t)
for t in thread_list:
    t.join()
# 为了确保结构正确 应该等待所有的线程运行完毕再打印money
print(money)

# 因为线程消耗较小,所以我们休息0.1S的时候,所有线程的tmp都已经被赋值好了,并且赋值的是100,所以最后为99,那么如何进行修改呢

 

 

 

from threading import Thread, Lock
import time

money = 100
mutex = Lock()


def task():
    global money
    mutex.acquire()
    tmp = money
    time.sleep(0.1)
    money = tmp - 1
    mutex.release()


thread_list = []
for i in range(100):  # 创建一百个线程
    t = Thread(target=task)
    t.start()
    thread_list.append(t)
for t in thread_list:
    t.join()
# 为了确保结构正确 应该等待所有的线程运行完毕再打印money
print(money)

 

 

 

2. 验证多线程作用

"""
两个大前提
  CPU的个数
    单个
    多个
  任务的类型
    IO密集型
    计算密集型
"""
# 单个CPU
    多个IO密集型任务
      多进程:浪费资源 无法利用多个CPU
    多线程:节省资源 切换+保存状态
  多个计算密集型任务
      多进程:耗时更长 创建进程的消耗+切换消耗
    多线程:耗时较短 切换消耗
# 多个CPU
    多个IO密集型任务
      多进程:浪费资源 多个CPU无用武之地
    多线程:节省资源 切换+保存状态
  多个计算密集型任务
      多进程:利用多核 速度更快
    多线程:速度较慢
结论:多进程和多线程都有具体的应用场景 尤其是多线程并不是没有用!!!

 

 

  2.2  计算密集型 

代码验证

from multiprocessing import Process
import os
import time


def work():
    res = 1
    for i in range(1, 100000):
        res *= i


if __name__ == '__main__':
    start_time = time.time()
    process_list = []
    for i in range(16):
        process_obj = Process(target=work)
        process_obj.start()
        process_list.append(p)
    for p in process_list:
        p.join()
    print('总耗时:%s' % (time.time() - start_time))

 

 

 

 

from threading import Threadimport os
import time


def work():
    res = 1
    for i in range(1, 100000):
        res *= i


if __name__ == '__main__':
    start_time = time.time()
    thread_list = []
    for i in range(16):
        thread_obj = Thread(target=work)
        thread_obj.start()
        thread_list.append(thread_obj)
    for thread_obj in thread_list:
        thread_obj.join()
    print('总耗时:%s' % (time.time() - start_time))

 

 

 

"""
计算密集型 
    多进程
        4.7536633014678955
    多线程
        36.5627920627594
    两者差了一个数量级(越多差距越大)       
结论
    多进程更好
"""

  

  2.3 IO密集型

from multiprocessing import Process
import os
import time

def work():
    time.sleep(2)   # 模拟纯IO操作


if __name__ == '__main__':
    start_time = time.time()
    p_list = []
    for i in range(100):
        process_obj = Process(target=work)
        process_obj.start()
    for p in p_list:
        p.join()
    print('总耗时:%s' % (time.time() - start_time))

 

 

 

from threading import Thread
import os
import time

def work():
    time.sleep(2)   # 模拟纯IO操作


if __name__ == '__main__':
    start_time = time.time()
    thread_list = []
    for i in range(100):
        thread_obj = Thread(target=work)
        thread_obj.start()
    for thread_obj in thread_list:
        thread_obj.join()
    print('总耗时:%s' % (time.time() - start_time))

 

 

 

"""
IO密集型
    多线程
        总耗时:0.009024620056152344
    多进程
        总耗时:0.44246482849121094
    两者差了两个数量级
结论
    多线程更好
"""

 

3. 死锁现象

# 锁就算掌握了如何抢 如何放 也会产生死锁现象
from threading import Thread, Lock
import time

# 产生两把(复习 面向对象和单例模式):每天都可以写写单例啊 算法啊...
mutexA = Lock()
mutexB = Lock()


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()
        mutexA.release()

    def func2(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()
"""锁不能轻易使用并且以后我们也不会在自己去处理锁都是用别人封装的工具"""

 

 

 

4. 信号量

from threading import Thread, Semaphore
import time
import random

semaphore_obj = Semaphore(5)  # 创建一个有五个坑位(带门的)的公共厕所


def task(name):
    semaphore_obj.acquire()  # 抢锁
    print('%s python cat' % name)
    time.sleep(random.randint(1, 5))
    semaphore_obj.release()  # 放锁


for i in range(1, 8):
    t = Thread(target=task, args=('cat%s号' % i, ))
    t.start()

# 会将5个显示出来,然后慢慢处理其他的数据

 

 

 

 

 

 

5. 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(5):
    t = Thread(target=car, args=('python cat %s' % i,))
    t.start()

 

 

 

6. 进程池与线程池


from concurrent.futures import ProcessPoolExecutor
import time

#
进程池 pool = ProcessPoolExecutor(5) # 进程池进程数默认是CPU个数 也可以自定义 '''上面的代码执行之后就会立刻创建五个等待工作的进程''' def task(n): time.sleep(2) print(n) return '任务的执行结果:%s'%n**2 def func(*args, **kwargs): print(args[0].result()) if __name__ == "__main__": for i in range(6): pool.submit(task, i).add_done_callback(func)

 

 

 

from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
import time

# 线程池
pool = ThreadPoolExecutor(5)  # 线程池线程数默认是CPU个数的五倍 也可以自定义
'''上面的代码执行之后就会立刻创建五个等待工作的线程'''


def task(n):
    time.sleep(2)
    print(n)
    return '任务的执行结果:%s' % n ** 2


def func(*args, **kwargs):
    # print(args, kwargs)
    print(args[0].result())


if __name__ == "__main__":
    for i in range(6):
        res = pool.submit(task, i)  # 朝线程池中提交任务(异步)
        print(res.result())  # 同步提交(获取任务的返回值)
        '''不应该自己主动等待结果 应该让异步提交自动提醒>>>:异步回调机制'''
        pool.submit(task, i).add_done_callback(func)
        """add_done_callback只要任务有结果了 就会自动调用括号内的函数处理"""

 

 

 

"""
任务的提交方式
    同步
        提交任务之后原地等待任务的结果
    异步
        提交任务之后不原地等待任务的结果 结果通过反馈机制自动获取
"""

 

7. 协程

"""
进程:资源单位
线程:执行单位
协程:单线程下实现并发

并发的概念:切换+保存状态

首先需要强调的是协程完全是程序员自己意淫出来的名词!!!
对于操作系统而言之认识进程和线程
协程就是自己通过代码来检测程序的IO操作并自己处理 让CPU感觉不到IO的存在从而最大幅度的占用CPU

类似于一个人同时干接待和服务客人的活 在接待与服务之间来回切换!!!
"""

 

# 保存的功能 我们其实接触过  yield 但是无法做到检测IO切换
from gevent import monkey;monkey.patch_all()  # 固定编写 用于检测所有的IO操作
from gevent import spawn
import time


def play(name):
    print('%s play 1' % name)
    time.sleep(5)
    print('%s play 2' % name)


def eat(name):
    print('%s eat 1' % name)
    time.sleep(3)
    print('%s eat 2' % name)


start_time = time.time()
g1 = spawn(play, 'jason')
g2 = spawn(eat, 'jason')
g1.join()  # 等待检测任务执行完毕
g2.join()  # 等待检测任务执行完毕
print('总耗时:', time.time() - start_time)  # 正常串行肯定是8s+
# 5.014967203140259  代码控制切换

 

 

 

8. 基于协程实现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()

"""
终极结论
    python可以通过开设多进程 在多进程下开设多线程 在多线程使用协程
    从而让程序执行的效率达到极致!!!
    
    但是实际业务中很少需要如此之高的效率(一直占着CPU不放)
    因为大部分程序都是IO密集型的

    所以协程我们知道它的存在即可 几乎不会真正去自己编写
"""


 

posted @ 2022-04-21 18:43  thrombus  阅读(27)  评论(0)    收藏  举报