Day37

今日总结

GIL与普通互斥锁的区别

# 验证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)  # 0

# 不同数据加不同的锁
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()
t_list = []
for i in range(100):  # 创建一百个线程
    t = Thread(target=task)
    t.start()
    t_list.append(t)
for t in t_list:
    t.join()  # 为了确保结构正确 应该等待所有的线程运行完毕再打印money
print(money)  # 0

# 在实际工作中几乎不用考虑GIl的存在,GIl是一个纯理论知识,应用面较窄

多线程作用

# 多线程与多进程各有千秋,有不同的适用场景
# 单核
	# 多个IO密集型任务
    多进程:浪费资源 无法利用多个cpu
    多线程:节省资源 切换+保存状态
    # 多个计算密集型任务
    多进程:耗时更长 创建进程时消耗、切换消耗
    所线程:耗时较短 切换消耗
  
# 多个CPU
	# 多个IO密集型任务
  	多进程:浪费资源 其他CPU闲置
    多线程:节省资源 切换+保存状态
    # 多个计算密集型任务
  	多进程:利用多核 速度更快
    多线程:速度较慢
        
# 在多核(12)环境下验证多线程与多进程的适用问题   
# 可以通过os模块中的cpu_count()方法查看计算机CPU个数

from threading import Thread
from multiprocessing import Process
import time

# 计算密集型任务
def work():
    res = 1
    for i in range(1, 10000):
        res *= i


if __name__ == '__main__':
    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()  # 总耗时:0.34558653831481934
    # 多线程
    t_list = []
    for i in range(12):
        t = Thread(target=work)
        t.start()
        t_list.append(t)
    for t in t_list:
        t.join()  # 总耗时:0.24379849433898926
    print('总耗时:%s' % (time.time() - start_time))
    
# 由上可知,多线程耗时更短
    
# IO密集型任务
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()  # 总耗时:0.013962030410766602
    # 多进程
    p_list = []
    for i in range(100):
        p = Process(target=work)
        p.start()
    for p in p_list:
        p.join()  # 总耗时:1.4700865745544434
    print('总耗时:%s' % (time.time() - start_time))
    
# 由上可知,多线程耗时更短,差距巨大

死锁

# 锁就算掌握了如何抢 如何放 也会产生死锁现象
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()
'''
	线程1抢到A锁,线程2等着,接着1拿到B锁,释放A锁,等了2s,线程2抢到A锁,B锁没有被及时释放就死锁了
'''

信号量

信息量在不同的知识体系中,功能是不同的:在并发编程中信号量意思是多把互斥锁;在django框架中信号量意思是达到某个条件自动触发特定功能

"""
如果将自定义互斥锁比喻成是一张床
那么信号量相当于是n张床
"""
from threading import Thread, Semaphore
import time
import random

sp = Semaphore(5)  # 创建5张床


def task(name):
    sp.acquire()
    print('%s睡觉ing' % name)
    time.sleep(random.randint(1, 5))
    sp.release()


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

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()

进程池与线程池

'''
	池:
		保证计算机硬件安全的情况下提升程序的运行效率
	进程池:
		提前创建好固定数量的进程,后续反复使用这些进程
	线程池:
		提前创建好固定数量的线程,后续反复使用这些线程
	如果任务超出了池子里面的最大进程或线程数,则原地等待

	进程池和线程池虽然降低了程序的运行效率,但是保证了硬件的安全
'''
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
# 线程池
pool = ThreadPoolExecutor(5)  # 创建五个待命的线程,其中参数可以自定义,默认是CPU个数*5
# 异步回调机制
pool.submit(task, i).add_done_callback(func)  # 自动提交提醒

# 进程池
pool = ProcessPoolExecutor(5)  # 创建五个待命的进程,其中参数可以自定义,默认是CPU的个数
# 异步回调机制
pool.submit(task, i).add_done_callback(func)

协程

# 操作系统只认识进程与线程,协程是认为臆想的名词,作用:单线程下实现并发,通过代码来检测程序的IO操作并自己处理 让CPU感觉不到IO的存在从而最大幅度的占用CPU

from gevent import monkey;monkey.patch_all()  # 固定格式 用于检测所有的IO操作
from gevent import spawn
import time


def func1():
    print('1 is running')
    time.sleep(5)
    print('1 is over' )


def func2():
    print('2 is running' )
    time.sleep(3)
    print('2 is over' )


start_time = time.time()
g1 = spawn(func1)
g2 = spawn(func2)
g1.join()  # 等待检测任务执行完毕
g2.join()
print('总耗时:', time.time() - start_time)  # 据经验,串行耗时一定是8s+
# 总耗时: 5.031261682510376  实际却是并发

基于协程实现TCP服务端并发

from gevent import monkey;monkey.patch_all()
from gevent import spawn
import socket


def communication(sock):
    while True:
        data = sock.recv(1024)
        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()
        spawn(communication, sock)

g1 = spawn(get_server)
g1.join()

'''
	python可以通过开设多进程 在多进程下开设多线程,在多线程使用协程,从而让程序执行的效率达到极致
	但是实际业务中很少需要这么高的效率,因为大部分程序都是IO密集型的,所以协程了解即可
'''

网络编程结束啦,感觉收获良多,明天就学前端了,希望我还没忘光以前学的前端吧

posted @ 2022-04-22 00:13  有梦想的阿九  阅读(35)  评论(0)    收藏  举报