并发编程

并发编程

1 操作系统发展史

  1. 手工操作--穿孔卡片
    • 用户独占全机
    • CPU的利用不充分
  2. 批处理
    • 把一个操作整个写到磁带中,以后要进行这个操作直接拿磁带,读入即可
    • 脱机批处理
    • 联机批处理
  3. 多道程序系统
    • 当一道程序因I/O请求而暂停时,CPU便立即转去运行另一道程序
    • 各道程序轮流用CPU,并交替运行
  4. 分时系统
    • 多个程序在运行,时间片的概念,CPU执行完固定的时间,就会转去另一个程序
  5. 通用操作系统
    • 多道批处理系统,分时系统

IO操作:(不占用CPU)

  • 键盘输入,从硬盘拿数据,从网络发送数据----》都叫输入
  • 显示器显示,写到硬盘,从网络发送数据-----》都叫输出

2 进程基础

  • 狭义定义:进程是正在运行的程序的实例(an instance of a computer program that is being executed)
  • 广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程即是基本的分配单元

进程是资源分配的最小单位,线程是CPU执行的最小单位

一个程序运行,最少有一个进程

一个进程里最小有一条线程

  • 进程和程序的区别:
    • 程序可以作为一种软件资料长期存在,而进程是有一定生命期的。
    • 程序是永久的,进程是暂时的。
  • 进程的状态
    • 就绪态:可以被CPU调度执行,但还没有执行,排着队
    • 运行态:在CPU中运行,正在运行(如果到了时间片也会被调度,调度出去的程序是就绪态)
    • 阻塞态:IO操作,吧数据加载到内存中

3 并发和并行

  • 并发:你在跑步,鞋带开了,停下跑步,系鞋带,系完后,继续跑步。在一个时间段内来看,同时干了多件事
    • 单核下的并发
  • 并行:你在跑步,同时用随身听听歌。同一时刻,在干多件事
    • 只有多核才涉及并行

4 如何开启多进程

from multiprocessing import Process


#如果在win下开多进程,必须写main,否则报错
import time
def wirte_file(s):

    time.sleep(5)
    with open('a.txt','a') as f:
        f.write(s)
        f.write('\n')

if __name__ == '__main__':
    time.sleep(5)
    # wirte_file()
    # 开启多进程的第一个方式
    p=Process(target=wirte_file,args=['lqz is nb'])
    # 执行该进程
    p.start()


    # 又开了一个进程
    p1 = Process(target=wirte_file,args=['egon is dsb'])
    # 执行该进程
    p1.start()

5 进程调度算法

  • 先来先服务调度算法
  • 短作业优先调度算法
  • 时间片轮转算法
  • 多级反馈队列

6 同步异步,阻塞非阻塞

  1. 同步,异步:指的是消息通知机制

    1. 同步调用:提交了以后一直等待返回结果
    2. 异步调用:提交了以后,返回一个标志,等待执行完成后,有消息通知
  2. 阻塞,非阻塞:程序在等待调用结果的状态

  • 同步阻塞:打电话买书,老板去找书,电话不挂,我也一直在等。

  • 同步非阻塞:打电话买书,老板去找书,电话不挂,但我一边在干别的事。

  • 异步阻塞:打电话买书,老板找到书再回电话给我,我就干等着,不干别的事。

  • 异步非阻塞:打电话买书,老板找到书再回电话给我,在回电话的过程我去干别的事

7 Process类的参数

'''
target=None, 你要执行的任务,函数
name=None,  进程名
args=(),    以位置参数给任务(函数)传递参数
kwargs={}   以关键字的形式给任务(函数)传递参数
'''

from multiprocessing import Process


def take(name, age):
    print(name)
    print(age)


if __name__ == '__main__':
    p = Process(target=take, args=('郑诗楠', 18), name= 'process01')
    p.start()

8 Process类的方法和属性

'''
###方法
p.start()  启动进程,必须调用start
p.run()    实际在执行进程时,执行的是run方法,但调用run不会开进程
p.join()   等待p进程执行完成,再继续执行主进程
p.terminate()   通知操作系统去杀死p这个进程,不是立刻将进程结束
p.is_alive()   查看进程是否存活,返回True False
'''
'''
###属性
print(p.name) # 进程名字
print(p.pid)  # 进程id号
p.daemon=True  #将其设置为守护进程,主进程结束,子进程也结束,必须在start之前调用
'''

8.1 主进程和子进程的进程号

'''
如果有p对象,就是用p.pid获取进程id号
如果没有p对象,就是用os模块的
os.getpid() #当前进程id号
os.getppid() #父进程id号
'''
from multiprocessing import Process
import os


def take(name, age):
    print(name)
    print(age)
    print('当前进程的id', os.getpid())
    print('当前进程父进程的id', os.getppid())


if __name__ == '__main__':
    p = Process(target=take, args=('郑诗楠', 18))
    p.start()

    print('当前p进程的id', p.pid)
    print('当前py文件进程的id', os.getpid())
    print('当前进程父进程-pycharm的id', os.getppid())

8.2 同时开启多个进程

'''
#开启多个进程
#当在创建进程的时候join,那么所创建的多个进程会一个一个执行,主进程会等创建的进程完毕后(一个一个轮着执行)继续执行
#当多个进程全部创建完成后,在同一join,那么多个进程会同时执行,主进程会等创建的进程完毕后(同时执行)继续执行
'''
from multiprocessing import Process
import time


def task():
    num = 0
    time.sleep(1)
    for i in range(100):
        num+=i
    print(num)


if __name__ == '__main__':
    ll = []
    for i in range(5):
        p = Process(target=task)
        p.start()
        # p.join()
        # ll.append(p)

    # [p.join() for p in ll]

8.3 开启进程的另一种方法

'''
通过继承Process类的方法来实现,重写run方法,run方法就只要执行的任务,实例化得到对象,通过调用start方法来开启进程
'''

from multiprocessing import Process

class MyProcess(Process):
    def __init__(self, name1, age):
        self.name1 = name1
        self.age = age
        super().__init__()

    def run(self) -> None:
        print(self.name1)
        print(self.age)


if __name__ == '__main__':
    p = MyProcess(name1='郑诗楠', age=18)
    p.start()

8.4 进程之间数据隔离

from multiprocessing import Process

def task():
    global i
    i = 1
    print('子进程中的', i)


if __name__ == '__main__':
    i = 100

    p = Process(target=task)
    p.start()
    p.join()

    print('主进程中的', i)

9 高并发的tcp服务端

import socket
from multiprocessing import Process


def talk(sock,addr):
    print('客户端连接成功',addr)
    while True:
        try:
            data = sock.recv(1024)
            if len(data) == 0:
                break
            print('从客户端收到了:', data)
            sock.send(data.upper())
        except Exception as e:
            print(e)
            break
    sock.close()
if __name__ == '__main__':
    server = socket.socket()
    server.bind(('127.0.0.1', 81))
    server.listen(5)
    while True:
        print('等待客户端连接')
        sock, addr = server.accept()
        p=Process(target=talk,args=[sock,addr])
        p.start()
    server.close()

10 进程同步(进程锁Lock方法)

'''
多个进程操作同一个数据(文件中的数据,而不是内存中的数据:)
lock=Lock()
将Lock()实例化得到对象lock传给子进程
加锁:lock.acquire()
解锁:lock.release()
'''
import time
import json
from multiprocessing import Process,Lock


## 查询票余额
def check(i):
    with open('ticket', 'rt', encoding='utf-8') as f:
        res = json.load(f)
    print('%s:在查询票,票还剩%s张' % (i, res['count']))
    if res['count'] > 0:
        return True


## 抢票,票的余额减一

def buy(i):
    with open('ticket', 'rt', encoding='utf-8') as f:
        res = json.load(f)
    time.sleep(1)  # 模拟查票延迟
    if res['count'] > 0:
        print('%s现在要买了,票数还是大于0'%i)
        res['count'] -= 1
        time.sleep(2)  # 模拟买票延迟
        with open('ticket', 'wt', encoding='utf-8') as f1:
            json.dump(res, f1)
        print('%s这个人购票成功' % i)
    else:
        print('%s这个人购票失败,票不够了,买不了了' % i)


def task(i,lock):
    res = check(i)
    if res:
        lock.acquire()
        buy(i)
        lock.release()


##模拟10个人买票

if __name__ == '__main__':
    lock=Lock()
    for i in range(10):
        p = Process(target=task, args=[i,lock ])
        p.start()

11 进程Queue介绍

  1. 进程间数据隔离,两个进程进行通信,借助于Queue
  2. 进程间通信:IPC
    • 借助于Queue实现进程间通信
    • 借助于文件
    • 借助于数据库
    • 借助于消息队列:rabbitmq,kafka

11.1 基本使用

'''
from multiprocessing import Queue  # 导入
queue = QUeue(3)  # 实例化一个对象,数字表示queue的最大值

queue.put()  # 放值 block:是否阻塞  timeout:等待时间
queue.get()  # 取值 block:是否阻塞  timeout:等待时间

queue.put_nowait()  # 放值时不等待,如果满了就直接报错
queue.get_nowait()  # 取值时不等待,如果空了就直接报错

queue.full()  # 查看queue是否满
queue.empty()  # 查看queue是否空
queue.qsize()  # 查看queue中还有几个值
'''
from multiprocessing import Process, Queue


if __name__ == '__main__':
    queue = Queue(3)
    queue.put('zsn')
    queue.put('1')
    queue.put(3)

    print(queue.full())
    print(queue.empty())
    print(queue.qsize())

    print(queue.get())
    print(queue.get())
    print(queue.get())

1.2 通过Queue实现进程间通信

from multiprocessing import Process,Queue


import os
import time

def task(queue):
    print('我这个进程%s开始放数据了'%os.getpid())
    time.sleep(10)
    queue.put('lqz is handsome')
    print('%s我放完了' % os.getpid())


if __name__ == '__main__':
    #不写数字,表示可以任意长度
    queue=Queue()
    p=Process(target=task,args=[queue,])
    p.start()

    res=queue.get()  #会卡在这
    print(res)

1.3 批量生产数据放入Queue再批量取出

from multiprocessing import Process,Queue
import os

def get_task(queue):
    res=queue.get()
    print('%s这个进程取了数据:%s'%(os.getpid(),res))


def put_task(queue):
    queue.put('%s:放了数据'%os.getpid())

if __name__ == '__main__':
    queue=Queue(1)
    p1=Process(target=put_task,args=[queue])
    p2=Process(target=put_task,args=[queue])
    p1.start()
    p2.start()


    p3=Process(target=get_task,args=[queue])
    p4=Process(target=get_task,args=[queue])
    p3.start()
    p4.start()




12 生产者消费者模型(重点)

from multiprocessing import Process, Queue
# import os
#
# import time
# import random
# def producer(queue):
#     # 生产的东西,放到Queue中
#     for i in range(10):
#         data = '%s这个厨师,整了第%s个包子' % (os.getpid(), i)
#         print(data)
#         # 模拟一下延迟
#         time.sleep(random.randint(1,3))
#         queue.put('第%s个包子'%i)
#
#
# def consumer(queue):
#     # 消费者从queue中取数据,消费(吃包子)
#     while True:
#
#         res=queue.get()
#         # 模拟一下延迟
#         time.sleep(random.randint(1, 3))
#         print('%s这个消费者,吃了%s'%(os.getpid(),res))
#
#
#
# if __name__ == '__main__':
#     queue=Queue(3)
#     p=Process(target=producer,args=[queue,])
#     p.start()
#
#     p1=Process(target=consumer,args=[queue,])
#     p1.start()


###### 改良(生产者以及不生产东西了,但是消费者还在等着拿)
# import os
#
# import time
# import random
# def producer(queue):
#     # 生产的东西,放到Queue中
#     for i in range(10):
#         data = '%s这个厨师,整了第%s个包子' % (os.getpid(), i)
#         print(data)
#         # 模拟一下延迟
#         time.sleep(random.randint(1,3))
#         queue.put('第%s个包子'%i)
#     # 生产完了,在queue中放一个None
#     queue.put(None)
#
#
# def consumer(queue):
#     # 消费者从queue中取数据,消费(吃包子)
#     while True:
#
#         res=queue.get()
#         if not res:break # 如果去到空,说明打烊了(生产者不生产了),退出
#         # 模拟一下延迟
#         time.sleep(random.randint(1, 3))
#         print('%s这个消费者,吃了%s'%(os.getpid(),res))
#
#
#
# if __name__ == '__main__':
#     queue=Queue(3)
#     p=Process(target=producer,args=[queue,])
#     p.start()
#
#     p1=Process(target=consumer,args=[queue,])
#     p1.start()


#### 把put none 放在主进程中执行
import os

# import time
# import random
# def producer(queue):
#     # 生产的东西,放到Queue中
#     for i in range(10):
#         data = '%s这个厨师,整了第%s个包子' % (os.getpid(), i)
#         print(data)
#         # 模拟一下延迟
#         time.sleep(random.randint(1,3))
#         queue.put('第%s个包子'%i)
#
#
#
# def consumer(queue):
#     # 消费者从queue中取数据,消费(吃包子)
#     while True:
#
#         res=queue.get()
#         if not res:break # 如果去到空,说明打烊了(生产者不生产了),退出
#         # 模拟一下延迟
#         time.sleep(random.randint(1, 3))
#         print('%s这个消费者,吃了%s'%(os.getpid(),res))
#
#
#
# if __name__ == '__main__':
#     queue=Queue(3)
#     p=Process(target=producer,args=[queue,])
#     p.start()
#
#     p1=Process(target=consumer,args=[queue,])
#     p1.start()
#
#     # 如果把put None放在这,会有问题
#     # 主进程会先执行这句话,消费进程读到None,直接结束,生产者进程没有结束,于是生产一直在生产,消费已经不消费了
#     # 直到Queue满了,就一直卡在这了
#     # queue.put(None)
#
#     ### 现在就要放在这,你把问题解决
#     p.join()
#     queue.put(None)



12.1 多个生产者多个消费者的生产者消费者模型

# 多个生产者在生产,多个消费者在消费
# import time
# import random
# def producer(queue,food):
#     # 生产的东西,放到Queue中
#     for i in range(10):
#         data = '%s这个厨师,做了第%s个%s' % (os.getpid(), i,food)
#         print(data)
#         # 模拟一下延迟
#         time.sleep(random.randint(1,3))
#         queue.put('第%s个%s'%(i,food))
#
#
# def consumer(queue):
#     # 消费者从queue中取数据,消费(吃包子)
#     while True:
#         res=queue.get()
#         if not res:break # 如果去到空,说明打烊了(生产者不生产了),退出
#         # 模拟一下延迟
#         time.sleep(random.randint(1, 3))
#         print('%s这个消费者,吃了%s'%(os.getpid(),res))
#
#
#
# if __name__ == '__main__':
#     queue=Queue(3)
#     ##起了三个生产者
#     p1=Process(target=producer,args=[queue,'包子'])
#     p2=Process(target=producer,args=[queue,'骨头'])
#     p3=Process(target=producer,args=[queue,'泔水'])
#     p1.start()
#     p2.start()
#     p3.start()
#
#
#
#     # 起了两个消费者
#     c1=Process(target=consumer,args=[queue,])
#     c2=Process(target=consumer,args=[queue,])
#     c1.start()
#     c2.start()
#
#     ##等三个生产者都生产完,放三个None
#     p1.join()
#     p2.join()
#     p3.join()
#     queue.put(None)
#     queue.put(None)
#     queue.put(None)

##如果消费者多,比生产者多出来的消费者不会停

import time
import random


def producer(queue, food,name):
    # 生产的东西,放到Queue中
    for i in range(10):
        data = '%s:这个厨师,做了第%s个%s' % (name, i, food)
        print(data)
        # 模拟一下延迟
        time.sleep(random.randint(1, 3))
        queue.put('第%s个%s' % (i, food))


def consumer(queue,name):
    # 消费者从queue中取数据,消费(吃包子)
    while True:
        try:
            res = queue.get(timeout=20)
            # 模拟一下延迟
            time.sleep(random.randint(1, 3))
            print('%s这个消费者,吃了%s' % (name, res))
        except Exception as e:
            print(e)
            break


if __name__ == '__main__':
    queue = Queue(3)
    ##起了三个生产者
    p1 = Process(target=producer, args=[queue, '包子','egon'])
    p2 = Process(target=producer, args=[queue, '骨头','lqz'])
    p3 = Process(target=producer, args=[queue, '泔水','jsason'])
    p1.start()
    p2.start()
    p3.start()

    # 起了两个消费者
    c1 = Process(target=consumer, args=[queue, '孟良'])
    c2 = Process(target=consumer, args=[queue,'池劲涛' ])
    c3 = Process(target=consumer, args=[queue,'池劲涛' ])
    c4 = Process(target=consumer, args=[queue,'池劲涛' ])
    c1.start()
    c2.start()
    c3.start()
    c4.start()

13 进程间数据共享(了解)

from multiprocessing import Process,Manager,Lock

# 魔法方法:类内以__开头__结尾的方法,都叫魔法方法,某种情况下会触发它的执行
'''
__init__ :类()触发
__new__:
__getattr__
__setattr__
__getitem__
__setitem__

'''

# def task(dic,lock):
#     # lock.acquire()
#     # dic['count']-=1
#     # lock.release()
#     with lock:
#         dic['count'] -= 1
#
# if __name__ == '__main__':
#     lock = Lock()
#     with Manager() as m:
#         # 如果直接定义dict,这个dict在多个进程中其实是多份,进程如果改,只改了自己的
#         #如果定义的是m.dict({'count': 100}),多个进程之间就可以共享这个数据
#         dic = m.dict({'count': 100})
#
#         p_l = []
#         for i in range(100):
#             p = Process(target=task, args=(dic, lock))
#             p_l.append(p)
#             p.start()
#         for p in p_l:
#             p.join()





def task(dic,lock):
    with lock:
        dic['count'] -= 1

if __name__ == '__main__':
    lock = Lock()
    dic={'count':100}
    p_l = []
    for i in range(100):
        p = Process(target=task, args=(dic, lock))
        p_l.append(p)
        p.start()
    for p in p_l:
        p.join()



    print(dic)



14 线程概念

如果把我们上课的过程看成一个进程的话,那么我们要做的是耳朵听老师讲课,手上还要记笔记,脑子还要思考问题,这样才能高效的完成听课的任务。而如果只提供进程这个机制的话,上面这三件事将不能同时执行,同一时间只能做一件事,听的时候就不能记笔记,也不能用脑子思考,这是其一;如果老师在黑板上写演算过程,我们开始记笔记,而老师突然有一步推不下去了,阻塞住了,他在那边思考着,而我们呢,也不能干其他事,即使你想趁此时思考一下刚才没听懂的一个问题都不行,这是其二

# 进程是资源分配的最小单位,线程是CPU调度的最小单位。每一个进程中至少有一个线程。

# 线程实际上是进程里干活的,每个进程运行,真正被CPU执行的是该进程里线程,线程执行是所需的资源都找所在的进程索要

# 进程和线程都是虚拟单位,只是为了我们更加方便的描述问题
from threading import Thread
from queue import Queue
import os
import time

def task():
    time.sleep(3)
    print('我是子线程执行的')
    print(os.getpid())


if __name__ == '__main__':
    # 启动线程

    ctime = time.time()
    t = Thread(target=task)
    t.start()
    # task()
    time.sleep(3)
    print(os.getpid())
    print(time.time() - ctime)

15 全局解释器锁(GIL)

  • 全局解释器锁,GIL锁(cpython解释器的问题)
    • 当年设计python的时候,CPU还是单核,没有多核的概念
    • python需要做垃圾回收机制(gc机制)
    • 垃圾回收线程进行垃圾回收
    • 设计了一个大锁(GIL锁),只有拿到这把锁的线程,才能执行
    • 同一时刻,在同一进程中,可以开多线程,但只能有一条线程在执行
    • 不能利用多核优势
  • 只针对cpython解释器(其他解释器和其他语言都没有这个GIL锁 )
  • 如果是计算密集型:开多进程
  • 如果是IO密集型:开多线程

16 开多线程的两方式

from threading import Thread
import time

#
# def task():
#     time.sleep(1)
#     print('我是子线程')
#
#
# if __name__ == '__main__':
#     t=Thread(target=task)
#     t.start()
#     print('我是主线程')
#
#


###第二种方式

class MyThread(Thread):
    def __init__(self,a):
        self.a=a
        super().__init__()
    def run(self):
        time.sleep(1)
        print('我是子线程',self.a)

if __name__ == '__main__':
    t=MyThread('aaaaa')
    t.start()
    print('我是主线程')

17 线程与进程的比较

17.1 pid 比较

17.2 开启速度比较

17.3 内存数据共享问题

18 Thread类的其他方法

'''
t.is_alive()  # 查看线程是否存活
t.getName()  # 获取线程名
t.setName('lqz')  # 设置线程名
t.join()   # 等待t这个子线程执行结束
t.setDaemon(True)  # 设置t为守护线程,主线程执行结束时子线程也结束
   
threading:模块下的一些方法
res=threading.currentThread()  # 返回当前线程对象
res=threading.enumerate()  # 返回当前进程中的线程对象
res=threading.activeCount()  # 返回当前正在运行的线程个数
res=threading.get_ident()  #  返回当前线程的id
'''

19 互斥锁(同步锁)

'''
threading 下的Lock模块,用法与进程锁相同
将Lock()实例化得到对象传给线程
加锁:lock.acquire()
解锁:lock.release()
'''

from threading import Thread, Lock
import time


def task(lock):
    global n
    lock.acquire()

    temp = n
    time.sleep(0.1)
    temp -= 1
    time.sleep(0.08)
    n = temp
    lock.release()
    
    print(n)


if __name__ == '__main__':
    n = 10
    ll = []
    lock = Lock()

    for i in range(5):
        t = Thread(target=task, args=(lock,))
        t.start()
        ll.append(t)

    for i in ll:
        i.join()

    print(n)

20 信号量

# 信号量可以理解为多把锁,允许同时有多个线程来更改数据

from threading import Thread, Semaphore
import time
import random


def task(sm, i):
    sm.acquire()
    print(f'{i}开始')
    time.sleep(random.random())
    print(f'{i}结束')
    sm.release()


if __name__ == '__main__':
    sm = Semaphore(5)
    for i in range(10):
        t = Thread(target=task, args=(sm, i))
        t.start()

21 Event事件

'''
一些线程需要等到其他线程执行完成后才能继续执行,类似于发射型号
比如一等线程等待另一个线程执行结束在继续执行
'''

from threading import Thread, Event
import time


def girl(event):
    print('女神在恋爱中')
    time.sleep(1)
    print('女神分手了')
    event.set()


def boy(i, event):
    print(f'最求者{i}:在等待')
    event.wait()
    print(f'最求者{i}:开始最求')


if __name__ == '__main__':
    event = Event()
    t = Thread(target=girl, args=(event,))
    t.start()

    for i in range(10):
        t1 = Thread(target=boy, args=(i, event))
        t1.start()

22 死锁

  • 所谓死锁:是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种相互等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程
  • 解决方法:可重入锁,递归锁
# 死锁的产生
from threading import Thread, Lock
import time


def eat1(lock_1, lock_2, name):
    lock_1.acquire()
    print(f'{name}:拿到了筷子')
    time.sleep(1)
    lock_2.acquire()
    print(f'{name}:拿到了面条')
    print('开始吃面')
    time.sleep(1)
    print(f'{name}:放下了面条')
    lock_2.release()
    print(f'{name}:放下了筷子')
    lock_1.release()


def eat2(lock_1, lock_2, name):
    lock_2.acquire()
    print(f'{name}:拿到了筷子')
    time.sleep(1)
    lock_1.acquire()
    print(f'{name}:拿到了面条')
    print('开始吃面')
    time.sleep(1)
    print(f'{name}:放下了面条')
    lock_1.release()
    print(f'{name}:放下了筷子')
    lock_2.release()


if __name__ == '__main__':
    lock_1 = Lock()
    lock_2 = Lock()
    for i in ['张三', '李四', '王五']:
        t = Thread(target=eat1, args=(lock_1, lock_2, i))
        t.start()

    for i in ['z', 's', 'n']:
        t = Thread(target=eat2, args=(lock_1, lock_2, i))
        t.start()

# 死锁解决: RLock:可重入锁,可重复acquire ,获得几次就需要释放几次
from threading import Thread, Lock, RLock
import time


def eat1(lock_1, lock_2, name):
    lock_1.acquire()
    print(f'{name}:拿到了筷子')
    time.sleep(1)
    lock_2.acquire()
    print(f'{name}:拿到了面条')
    print('开始吃面')
    time.sleep(1)
    print(f'{name}:放下了面条')
    lock_2.release()
    print(f'{name}:放下了筷子')
    lock_1.release()


def eat2(lock_1, lock_2, name):
    lock_2.acquire()
    print(f'{name}:拿到了面条')
    time.sleep(1)
    lock_1.acquire()
    print(f'{name}:拿到了筷子')
    print('开始吃面')
    time.sleep(1)
    print(f'{name}:放下了筷子')
    lock_1.release()
    print(f'{name}:放下了面条')
    lock_2.release()


if __name__ == '__main__':
    lock_1 = RLock()
    lock_2 = lock_1
    for i in ['张三', '李四', '王五']:
        t = Thread(target=eat1, args=(lock_1, lock_2, i))
        t.start()

    for i in ['z', 's', 'n']:
        t = Thread(target=eat2, args=(lock_1, lock_2, i))
        t.start()

23 线程队列(Queue)

  1. 线程Queue,解决线程间数据共享问题
  2. 线程间数据共享可以使用共享变量(可能会存在并发安全问题)
# 线程queue
# Queue:先进先出
#LifoQueue:后进先出
#PriorityQueue:优先级队列 数字越小,优先级越高
from threading import Thread
from queue import Queue,LifoQueue,PriorityQueue
# import time
# def task(queue):
#     time.sleep(3)
#     queue.put('lqz')
#
#
#
# if __name__ == '__main__':
#     queue=Queue()
#     t=Thread(target=task,args=[queue,])
#     t.start()
#
#
#     res=queue.get()
#     print(res)


if __name__ == '__main__':
    # quque1=Queue()
    #
    # quque1.put(1)
    # quque1.put(2)
    # print(quque1.get())



    # quque2=LifoQueue()
    # quque2.put(1)
    # quque2.put(2)
    # print(quque2.get())


    quque3=PriorityQueue()
    quque3.put((1,'lqz'))
    quque3.put((100,'egon'))
    # 数字越小,优先级越高
    print(quque3.get())

24 进程池,线程池

'''
# 线程池和进程池都在模块concurrent.futures下,ProcessPoolExecutor进程池,ThreadPoolExecutor线程池
submit  把任务提交到线程池/进程池里
shutdown  等待所有子进程或子线程结束,再继续运行
result		取到当前进程执行任务的返回值
map(了解)  map取代for循环的,第一个参数是要执行的任务,第二个参数,是一个可迭代对象,迭代一次的结果,会传给任务
add_done_callback:回调
'''
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
import time
import os


def task(n):
    print(os.getpid(), '开始执行')
    time.sleep(1)
    return n * n


def callback(result):
    print(result.result())

# 进程池
if __name__ == '__main__':
    # pool_p = ProcessPoolExecutor(3)
    # ll = []
    # for i in range(10):
    #     f = pool_p.submit(task, n =i)
    #     ll.append(f)

    # pool_p.shutdown()
    #
    # for l in ll:
    #     res = l.result()
    #     print(res)

    pool_p = ProcessPoolExecutor(3)
    for i in range(10):
        pool_p.submit(task, n=i).add_done_callback(callback)

    print('zhu')

# 线程池
if __name__ == '__main__':
    pool_p = ThreadPoolExecutor(3)
    for i in range(10):
        pool_p.submit(task, n=i).add_done_callback(callback)
	

25 协程介绍

协程是:程序级别的切换,单线程下实现并发
python的线程属于内核级别的,即由操作系统控制调度(如单线程遇到io或执行时间过长就会被迫交出cpu执行权限,切换其他线程运行)
单线程内开启协程,一旦遇到io,就会从应用程序级别(而非操作系统)控制切换,以此来提升效率(!!!非io操作的切换与效率无关)

26 greenlet模块

# 安装第三方模块:在命令行下
# pip3 insrall greenlet
# pip3 uninsrall greenlet   卸载第三方模块
# pip3 list  列出当前解释器环境下的第三方模块

# 手动切换任务
from greenlet import greenlet
def eat(name):
    print(name, '吃了一口')
    g2.switch(name)
    print(name, '吃了第二口')
    g2.switch()


def play(name):
    print(name, '玩了一下')
    g1.switch()
    print(name, '玩了第二下')


if __name__ == '__main__':
    g1 = greenlet(eat)
    g2 = greenlet(play)

    g1.switch('egon')

27 gevent模块

# gevent模块
import gevent
import time


def eat(name):
    print(name, '吃了一口')
    gevent.sleep(1)
    print(name, '吃了第二口')


def play(name):
    print(name, '玩了一下')
    gevent.sleep(2)
    print(name, '玩了第二下')


ctiem = time.time()

res1 = gevent.spawn(eat, 'egon')
res2 = gevent.spawn(play, 'egon')

# res1.join()  # 等待任务完成再继续执行后面的代码
# res2.join()

gevent.joinall([res1, res2])  # 等同于上面两句

print('主线程', ctiem - time.time())
# 使用原来的time的io,不会切,并且变成了串行
import gevent
import time
def eat(name):
    print(name, '在吃了一口')
    time.sleep(2)

    print(name, '在吃了第二口')


def play(name):
    print(name, '玩了一下')
    time.sleep(3)
    print(name, '玩了第二下')


res1 = gevent.spawn(eat, 'egon')
res2 = gevent.spawn(play, 'egon')


ctime=time.time()

gevent.joinall([res1,res2])   # 相当于上面那两句
print('主线程')
print(time.time()-ctime)
  • 猴子补丁
# 猴子补丁:把原来的io全都替换成gevent的io
from gevent import monkey;monkey.patch_all()
import gevent
import time


def eat(name):
    print(name, '在吃了一口')
    time.sleep(2)

    print(name, '在吃了第二口')


def play(name):
    print(name, '玩了一下')
    time.sleep(3)
    print(name, '玩了第二下')


res1 = gevent.spawn(eat, 'egon')
res2 = gevent.spawn(play, 'egon')

ctime=time.time()

gevent.joinall([res1, res2])
print('主线程')
print(time.time()-ctime)

28 asyncio

python的一个内置模块,在python3.4推出,由python作者主导,目的是为了解决python中GIL锁导致的性能下降的问题

import asyncio
import time
import threading
# 加了async关键字后,表示这个函数是协程函数
async def task():
    res=threading.current_thread().getName()
    print(res)
    print('xxx')
    await asyncio.sleep(2)
    print('协程执行完成')

async def task2():
    res=threading.current_thread().getName()
    print(res)
    print('2222')
    await asyncio.sleep(3)
    print('222协程执行完成')


ctime=time.time()
loop=asyncio.get_event_loop()

tasks=[task(),task2()]

loop.run_until_complete(asyncio.wait(tasks))
loop.close()
print(time.time()-ctime)
posted @ 2021-07-02 20:31  zheng-sn  阅读(56)  评论(0)    收藏  举报