并发编程(二)

并发编程(二)

1.GIL与普通互斥锁的区别

# 1.验证GIL的存在
from threading import Thread,Lock
import time

s = 100
def task():
    global s
    s -= 1

for i in range(100):  # 创建100个线程
    t = Thread(target=task)
    t.start()
print(s)  # 0

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

s = 100
mutex = Lock()
def task():
    global s
    mutex.acquire()
    num = s
    time.sleep(0.1)
    s = num - 1
    mutex.release()

t_list = []
for i in range(100):  # 创建100个线程
    t = Thread(target=task)
    t.start()
    t_list.append(t)

for t in t_list:
    t.join()  

print(s)  # 0

'''
抢锁放锁也有简便写法,就是with上下文管理
with mutex:
    pass
'''
# 3.GIL是一个纯理论知识,它的作用面很窄,仅限于解释器级别,所以我们想要保证数据的安全应该自定义互斥锁,使用别人封装
好的工具

2.验证多线程作用

# 1.两个前提
	1.CPU的个数(单个、多个)
    2.任务的类型(IO密集型、计算密集型)
# 2.单个CPU
	1.多个IO密集型任务
    	多进程:浪费资源,无法利用多个CPU
		多线程:节省资源,切换加保存状态
	2.多个计算密集型任务
    	多进程:耗时更长,创建进程的消耗加上切换的时间
		多线程:耗时较短,切换会消耗时间
# 3.多个CPU
	1.多个IO密集型任务
    	多进程:浪费资源,多个CPU没有用处
		多线程:节省资源,切换加保存状态
	2.多个计算密集型任务
    	多进程:利用多核,速度更快
		多线程:速度较慢
'''多进程与多线程都有具体的应用场景'''
# 4.代码演示
	# 计算密集型
    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()
        # print('总耗时:%s'%(time.time() - start_time))
        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.2672848701477051
多线程:总耗时:0.19750189781188965
'''
	# IO密集型
    from threading import Thread
    from multiprocessing import Process
    import os
    import time

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

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

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

'''
IO密集型
多进程:总耗时:0.012962579727172852
多线程:总耗时:0.4617276191711426
'''

3.死锁现象

from threading import Thread,Lock
import time

mutex1 = Lock()
mutex2 = Lock()

class MyThread(Thread):
    def run(self):
        self.f1()
        self.f2()

    def f1(self):
        mutex1.acquire()
        print(f'{self.name}抢到了第一个锁')
        mutex2.acquire()
        print(f'{self.name}抢到了第二个锁')
        mutex2.release()
        mutex1.release()

    def f2(self):
        mutex2.acquire()
        print(f'{self.name}抢到了第二个锁')
        time.sleep(2)
        mutex1.acquire()
        print(f'{self.name}抢到了第一个锁')
        mutex1.release()
        mutex2.release()

for i in range(20):
    t = MyThread()
    t.start()
    
'''
Thread-1抢到了第一个锁
Thread-1抢到了第二个锁
Thread-1抢到了第二个锁
Thread-2抢到了第一个锁
'''

4.信号量

# 信号量
	信号量在不同的知识体系中,展现出来的功能是不一样的,在并发编程中信号量的意思是多把互斥锁,在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,30):
    t = Thread(target=task,args=('oscar%s'%i,))
    t.start()

5.event事件

# event事件
	event事件就是子线程的运行可以由其他子线程决定
# 代码演示
from threading import Thread,Event
import time

event = Event()  # 类似于建了一个红绿灯

def light():
    print('红灯停')
    time.sleep(3)
    print('绿灯行')
    event.set()

def car(name):
    print(f'{name}正在等红灯')
    event.wait()
    print(f'{name}开车走了')

if __name__ == '__main__':
    t = Thread(target=light)
    t.start()
    for i in range(20):
        t = Thread(target=car,args=('oscar%s'%i,))
        t.start()

6.进程池与线程池

# 服务端三要素
	1.24小时不间断提供服务
    2.固定的IP和port
    3.支持高并发
# TCP服务端实现并发
	多进程:来一个客户端就打开一个进程
     多线程:来一个客户端就打开一个线程
# 进程池与线程池
	但是呢计算机硬件是有物理极限的,我们不可能无限制的创建进程和线程,所以就可以用池来保证计算机硬件安全的情况
下提升程序的效率。
    进程池:提前创建好固定数量的进程,后续反复使用这些进程
	线程池:提前创建好固定数量的线程,后续反复使用这些线程
     如果任务超出了池子里面的最大进程或线程数,就原地等待,虽然进程池和线程池降低了程序的运行效率,但是保证了硬件的
安全。
# 代码演示
'''线程池'''
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[0].result)

for i in range(20):
    pool.submit(task,i).add_done_callback(func)  # add_done_callback只要任务有结果了,就会自动调用括号内的函
数处理
'''进程池'''
pool = ProcessPoolExecutor(5)  # 进程池进程数默认是CPU个数,也可以自定义
pool.submit(task,i).add_done_callback(func)

7.协程

# 协程
	单线程下实现并发就是协程,协程不是官方名词,是程序员自定义的名称,对于操作系统而言只认识进程和线程,并不认
识协程,协程是自己通过代码来检测程序的IO操作并自己处理,让CPU察觉不到IO操作的存在,从而最大化的利用CPU。
# 代码演示
from gevent import monkey;monkey.patch_all()  # 固定编写,用于检查所有的IO操作
from gevent import spawn
import time

def play(name):
    print(f'{name}来了')
    time.sleep(5)
    print(f'{name}又来了')

def eat(name):
    print(f'{name}走了')
    time.sleep(2)
    print(f'{name}走远了')

start_time = time.time()
g1 = spawn(play, 'oscar')
g2 = spawn(eat, 'oscar')
g1.join()
g2.join()
print('总耗时',time.time() - start_time)  # 总耗时 5.033620357513428

这里是IT小白陆禄绯,欢迎各位大佬的指点!!!

posted @ 2022-04-21 18:13  陆禄绯  阅读(32)  评论(0)    收藏  举报