锁、池与协程

验证GIL的存在

from threading import Thread
money = 10


def task():
    global money
    tmp = money
    money = tmp - 1


t_list = []
for i in range(10):
    t = Thread(target=task)
    t.start()
    t_list.append(t)
for t in t_list:
    t.join()
# GIL锁会让线程依次执行
print(money)
# 0

GIL和互斥锁

from threading import Thread
import time
money = 10


def task():
    global money
    tmp = money
    time.sleep(0.1)  # 如果线程进入IO操作就会释放GIL锁
    money = tmp - 1


t_list = []
for i in range(10):
    t = Thread(target=task)
    t.start()
    t_list.append(t)
for t in t_list:
    t.join()

print(money)
# 9
'GIL不会影响程序层面的数据也不会保证数据安全,需要自己加锁'

from threading import Thread, Lock
import time
money = 10
mutex = Lock()


def task():
    mutex.acquire()  # 抢锁
    global money
    tmp = money
    time.sleep(0.1)  # 如果线程进入IO操作就会释放GIL锁
    money = tmp - 1
    mutex.release()  # 释放锁


t_list = []
for i in range(10):
    t = Thread(target=task)
    t.start()
    t_list.append(t)
for t in t_list:
    t.join()

print(money)
# 0
img

验证python多线程是否有用

1.单个CPU
1.1IO密集型
多进程:要申请额外的空间,消耗更多资源
多线程:不需要申请额外空间,通过多道技术

1.2计算密集型
多进程:要申请额外的空间,消耗更多资源,运行时间更长(总耗时+申请空间+拷贝代码+切换)
多线程:不需要申请额外空间,通过多道技术,运行时间更短(总耗时+切换)

2.多个CPU
2.1IO密集型
多进程:总耗时(单个进程的耗时+IO+申请空间+拷贝代码)
多线程:总耗时(单个进程的耗时+IO)术

2.2计算密集型
多进程:总耗时(单个进程的耗时)
多线程:总耗时(多个进程的综合)
'只有多个CPU的计算密集型多进程才更有优势'

3.多个CPU情况下
3.1计算密集型
from threading import Thread
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()
    p_list = []
    for i in range(10):
        p = Process(target=work)
        p.start()
        p_list.append(p)
    for p in p_list:
        p.join()
    print(time.time() - start_time)
    # 6.014247179031372
    t_list = []
    for i in range(10):
        t = Thread(target=work)
        t.start()
        t_list.append(t)
    for t in t_list:
        t.join()
    print(time.time() - start_time)
    # 23.872171878814697
'''
计算密集型
多进程:6.014247179031372
多线程:23.872171878814697
'''

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


if __name__ == '__main__':
    start_time = time.time()
    p_list = []
    for i in range(10):
        p = Process(target=work)
        p.start()
        p_list.append(p)
    for p in p_list:
        p.join()
    print(time.time() - start_time)
    # 1.3191983699798584
    t_list = []
    for i in range(10):
        t = Thread(target=work)
        t.start()
        t_list.append(t)
    for t in t_list:
        t.join()
    print(time.time() - start_time)
    # 1.0059502124786377
'''
IO密集型
多进程:1.3191983699798584
多线程:1.0059502124786377
'''

死锁现象

'''
虽然我们已经掌握了互斥锁的使用,但在实际项目中尽量少用,可能会出现锁死现象
'''

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()
        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(5):
    t = MyThread()
    t.start()
# Thread-1抢到了A锁
# Thread-1抢到了B锁
# Thread-1释放了B锁
# Thread-1释放了A锁
# Thread-1抢到了B锁
# Thread-2抢到了A锁
img

信号量

'''
1.信号量本质也是互斥锁,只不过它是多把锁
2.信号量在不同的知识体系中,意思可能有区别,在并发编程中,信号量就是多把互斥锁,在django中,信号量指的是达到某个条件自动触发(中间件)
'''
from threading import Thread, Semaphore
import time
import random
sp = Semaphore(3)  # 产生三把锁


class MyThread(Thread):
    def run(self):
        sp.acquire()
        print(self.name)
        time.sleep(random.randint(1, 3))
        sp.release()


for i in range(7):
    t = MyThread()
    t.start()
# Thread-1
# Thread-2
# Thread-3
# Thread-5Thread-4
#
# Thread-6
# Thread-7
#
# Process finished with exit code 0

event事件

'子进程\子线程之间可以彼此等待彼此'

from threading import Thread, Event
import time
event = Event()


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


def car(name):
    print('等红灯')
    event.wait()
    print(f'绿灯 {name}冲啊')


t = Thread(target=light)
t.start()
for i in range(5):
    t = Thread(target=car, args=(f'飙车党{i}',))
    t.start()
# 红灯停
# 等红灯
# 等红灯
# 等红灯
# 等红灯
# 等红灯
# 绿灯行
# 绿灯 飙车党0冲啊绿灯 飙车党1冲啊
# 绿灯 飙车党3冲啊
# 绿灯 飙车党4冲啊
#
# 绿灯 飙车党2冲啊

进程池和线程池

'''
1.在实际应用中是不是可以无限制的开进程和线程,如果开的进程和线程过多,会造成内存溢出
2.进程池计算提前创建好固定个数的进程供程序使用,后续不会再创建
3.线程池就是提前创建好固定个数的线程供程序使用,后续不会再创建
'''
from concurrent.futures import ProcessPoolExecutor
import os
import time
pool = ProcessPoolExecutor(5)  # 固定产生五个线程


def task(n):
    print(os.getpid())
    print(n)
    time.sleep(1)
    return '返回值'


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


if __name__ == '__main__':
    for i in range(3):
        res = pool.submit(task, 233)  # 朝池子中提交任务(异步)
        print(res.result())  # 同步
        # 16928
        # 233
        # 返回值
        # 23472
        # 233
        # 返回值
        # 11156
        # 233
        # 返回值
        '异步任务执行完成后有结果就会自动触发异步回调'
        pool.submit(task, 233).add_done_callback(func)
        # 13536
        # 233
        # 10116
        # 233
        # 22864
        # 233
        # func (<Future at 0x1acfd9c4400 state=finished returned str>,) {}
        # 返回值
        # func (<Future at 0x1acfd9e5b20 state=finished returned str>,) {}
        # 返回值
        # func (<Future at 0x1acfd9e5f70 state=finished returned str>,) {}
        # 返回值
img

协程

'''
协程:单线程下实现并发
在代码层面欺骗CPU,让CPU觉得代码中没有IO操作,实际上IO操作被我们自己写的代码检测 一旦有,立刻让代码执行别的
(该技术完全是程序员自己弄出来的,名字也是程序员自己起的)
核心:自己写代码完成切换+保存状态
'''
import time
from gevent import monkey; monkey.patch_all() 
# 固定编写 用于检测所有的IO操作(猴子补丁)
from gevent import spawn


def func1():
    print('func1 start')
    time.sleep(1)
    print('func1 over')


def func2():
    print('func2 start')
    time.sleep(2)
    print('func2 over')


if __name__ == '__main__':
    start_time = time.time()
    s1 = spawn(func1)  # 检测代码 一旦有IO自动切换(执行没有io的操作,变向的等待io结束)
    s2 = spawn(func2)
    s1.join()
    s2.join()
    print(time.time() - start_time)
    # func1 start
    # func2 start
    # func1 over
    # func2 over
    # 2.093316078186035

协程实现TCP服务端并发

客户端
import socket
from gevent import monkey; monkey.patch_all()
from gevent import spawn


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)


服务端
from threading import Thread, current_thread
import socket
client = socket.socket()
client.connect(('127.0.0.1', 8080))


def task():
    while True:
        res = current_thread().name
        client.send(res.encode('utf8'))
        data = client.recv(1024)
        print(data.decode('utf-8'))


for i in range(5):
    t = Thread(target=task)
    t.start()
posted @ 2022-08-11 20:29  无言以对啊  阅读(23)  评论(0)    收藏  举报