zoukankan      html  css  js  c++  java
  • 并发编程

    一、进程简介

    1.什么是并发、并行?

    并发:看起来像同时运行,内部:服务器来回切换者服务,速度很快,用户感觉不到

    并行:真正意义上的同时运行

    2.什么是同步异步?

    同步异步:表示任务的提交方式

    同步:任务提交之后,必须等待有返回结果,才继续执行下一步

    异步:任务提交之后,不等待,继续执行下一步,(返回结果通过其他方式调用)

    3.什么是阻塞非阻塞

    阻塞:阻塞态

    非阻塞:就绪态,运行态

    1565532165252

    4.程序的理想状态

    异步非阻塞

    二、进程

    windows 创建进程会将代码以模块的方式 从上往下执行一遍
    linux 会直接将代码完完整整的拷贝一份
    **windows 创建进程一定要在if name == 'main':代码内创建 否则报错

    1.创建进程原理:

    ​ 1.创建进程就是在内存中重新开辟一块内存空间
    ​ 将允许产生的代码丢进去

    ​ 2.一个进程对应在内存就是一块独立的内存空间

    ​ 3.进程与进程之间数据是隔离的 无法直接交互
    ​ 但是可以通过某些技术实现间接交互

    2.***进程中的方法(大概)

    multiprocessing import Process : 创建进程模块
    p.start() # 告诉操作系统帮你创建一个进程,操作系统随机创建
    p.join() # 主进程代码等待子进程运行结束
    current_process() # 进程号(用os的)
    os.getpid() # 获取子进程号
    os.getppid() # 获取父进程号
    p.terminate() # 杀死进程号
    p.is_alive() # 判断进程是否存在(bool值)
    p.daemon = Trun # 将主进程 设置为守护进程(放在创建进程的前面 p.start)
    Lock模块:加锁
    mutex.acquire() # 抢锁
    mutex.release() # 释放锁
    metex = Lock() # 创建锁

    3.创建进程的两种方式

    关键字:from multiprocessing import Process 进程模块

    创建进程:通过定义的方法或者类创建子进程对象,然后再创建进程用 对象.start()方法

    1.第一种方式,实例化Process方法

    from multiprocessing import  Process
    import time
    
    
    def test(name):
        print('%s 子进程开始' % name)
        time.sleep(2)
        print('%s 子进程结束' % name)
    
    if __name__ == '__main__':
        p = Process(target=test, args=('jeff',)) # 创建一个子进程对象
        p.start()  # 告诉操作系统帮你创建一个进程
        print('主程序')
    

    2.第二种方式,继承Process类重写run方法

    from multiprocessing import Process
    import time
    
    
    class MyProcess(Process):
        def __init__(self, name):
            super().__init__()
            self.name = name
    
        def run(self):
            print('%s 子进程开始' % self.name)
            time.sleep(2)
            print('%s 子进程结束' % self.name)
    
    if __name__ == '__main__':
        p = MyProcess('jeff')  # 创建进程对象
        p.start()  # 创建进程
        p.join()  # 等待创建的子进程结束,再执行下面的代码
        print('主进程 ')
    

    4.进程之间是隔离的(可以通过其他方式交互)

    from multiprocessing import Process
    import time
    
    
    money = 100
    
    def test():
        global money  # 声明修改全局变量
        money = 99999999
    
    
    if __name__ == '__main__':
        p = Process(target=test)
        p.start()
        p.join()  # 等待子进程结束在执行下面
        print(money)  # 结果:100 证明没有修改成功,进程之间是隔离的
    

    5.守护进程p.daemon = True

    守护进程p.daemon = True:主进程结束,子进程立马结束,这句话必须放在start()创建进程 前面

    p = Process(target=test,args=('QQ',)) # 创建子进程对象

    from multiprocessing import Process
    import time
    
    
    """
    守护进程:主进程结束,子进程立马结束
    """
    def test(name):
        print('%s 子进程活着' % name)
        time.sleep(2)
        print('%s 子进程死亡' % name)
    
    
    if __name__ == '__main__':
        p = Process(target=test,args=('QQ',))  # 创建子进程对象
        p.daemon = True  # 将该进程设置为守护 进程   这句话必须放在start()前面
        p.start()  # 创建进程
        time.sleep(1)
        print('qq主进程结束')
    

    6.杀死进程,判断存活

    p.terminate() : 杀死当前进程
    p.is_alive() :判断当前进程是否存活,返回值为bool类型

    from multiprocessing import Process,current_process
    import os
    import time
    
    
    def test(name):
        # print('%s is running'%name,current_process().pid)
        print('%s is running'%name,'子进程%s'%os.getpid(),'父进程%s'%os.getppid())
        time.sleep(3)
        print('%s is over'%name)
    
    
    if __name__ == '__main__':
        p = Process(target=test,args=('egon',))
        p.start()
        p.terminate()  # 杀死当前进程  其实是告诉操作系统帮你杀死一个进程
        time.sleep(0.1)
        print(p.is_alive())  # 判断进程是否存活
        # print('主',current_process().pid)
        print('主',os.getpid(),'主主进程:%s'%os.getppid())
    

    7.孤儿进程,僵尸进程

    一个电脑的进程号有限制的,每一个程序启动都会占用一个进程号

    init成为孤儿福利院

    僵尸进程:僵尸进程是当子进程比父进程先结束,而父进程又没有回收子进程。危害:致系统不能产生新的进程

    孤儿进程:在操作系统领域中,孤儿进程指的是在其父进程执行完成或被终止后仍继续运行的一类进程。这些孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。

    父进程回收子进程资源(进程号)的两种方式:
    1.join方法

    ​ 2.父进程正常死亡

    所有进程都会步入僵尸进程

    三.锁

    1.进程互斥锁

    多个进程同时间操作同一份文件,会造成数据絮乱

    ​ 处理方法:加锁处理,将并发变成串行,虽然降低了效率,但是提高了安全

    ​ 注意:1.锁不要轻易使用,容易造成死锁现象

    ​ 2.只在处理关键数据的部分加锁

    ​ 3.锁必须在主进程中产生,交给子进程使用

    Lock模块:加锁
    mutex.acquire() # 抢锁
    mutex.release() # 释放锁
    metex = Lock() # 创建锁,生成一把锁

    01买票,查票例子

    """
    买票思路:
    一.用户查询余票
    1.每个用户都可以同时查询余票,但是在查票买票的这一过程别人也可以买票

    二.用户买票
    1.如果有余票,则进入买票阶段
    2.用互斥锁,把数据锁上,同一时间只有一个用户可以操作数据
    """

    from multiprocessing import Process, Lock
    import time
    import json
    
    # 查票
    def search(name):
        with open('data', 'r', encoding='utf-8') as f:
            data = f.read()
            data_d = json.loads(data)  # 解序列化
            print('用户%s 查询余票为:%s' % (name, data_d.get('ticket')))
    
    
    # 买票
    def buy(name):
        with open('data', 'r', encoding='utf-8') as f:
            data = f.read()
            data_d = json.loads(data)
            time.sleep(1)   # 模拟网络延迟
            if data_d.get('ticket') > 0:     # 如果票数大于0就可以买票
                # 票数减一
                data_d['ticket'] -= 1
                # 更新票数,重新写入文件     文件操作
                with open('data', 'w', encoding='utf-8') as f:
                    json.dump(data_d, f)  # 转序列化
                print('用户%s抢票成功' % name)
            else:
                print('没票了')
    
    
    def run(name, mutex):
        search(name)
        mutex.acquire()   # 抢锁
        buy(name)
        mutex.release()  # 释放锁
    
    
    if __name__ == '__main__':
        mutex = Lock()  # 生成一把锁
        for i in range(10):
            p = Process(target=run, args=(i,  mutex))
            p.start()
    
    

    2.线程互斥锁

    当多个用户同一时间操作同一份数据的时候,容易造成数据错乱

    **多个数据操作同一份文件,第一时间应该想到上锁

    from threading import Thread,Lock
    import time
    
    
    """
    多个线程同一时间操作同一份文件,容易造成数据错乱
    """
    n =100
    
    def task(mutex):
        global n
        mutex.acquire()   # 抢锁
        tmp = n
        time.sleep(0.01)
        n = tmp-1
        mutex.release()   # 释放锁
    
    t_list = []  
    mutex = Lock()  # 生成锁
    for i in range(100):
        t = Thread(target=task, args=(mutex,))
        t.start()
        t_list.append(t)  # 子线程名字添加到列表
    for t in t_list:
        t.join()  # 确保子线程都执行完毕
    
    print(n)
    

    3.死锁

    两把锁的时候:你锁我,我锁你。。。。死锁

    例子说明:

    from threading import Thread,Lock
    import time
    
    mutexA = Lock()  # 生成A锁
    mutexB = Lock()  # 生成B锁
    
    class MyThread(Thread):
        def run(self):  # 创建线程自动触发run方法 run方法内调用func1 func2相当于也是自动触发
            self.func1()
            self.func2()
    
        def func1(self):
            mutexA.acquire()  #  1号抢到A锁,其他人在外面
            # self.name等价于current_thread().name
            print('%s抢到了A锁'%self.name)  
            mutexB.acquire()			  # 其他人在外面,1号抢到A锁继续抢到B锁
            print('%s抢到了B锁'%self.name)
            mutexB.release()			  # 1号释放了B锁
            print('%s释放了B锁'%self.name)
            mutexA.release()			  # 1号释放了A锁,这个时候2号抢到A锁
            print('%s释放了A锁'%self.name)
            
    
        def func2(self):
            mutexB.acquire()    		   # 1号继续抢到B锁
            print('%s抢到了B锁'%self.name)
            time.sleep(1)
            mutexA.acquire()# 这时,1号要抢A锁,但是A锁在2号手上,2号要抢B锁,B锁在1号手上,死锁
            print('%s抢到了A锁' % self.name)
            mutexA.release()
            print('%s释放了A锁' % self.name)
            mutexB.release()
            print('%s释放了B锁' % self.name)
    
    for i in range(10):   # 10个人抢锁
        t = MyThread()   #  生成线程对象
        t.start()   #  启动线程
    
    

    4.递归锁

    RLock 递归锁模块

    acquire 抢锁

    release 释放锁

    RLock可以被第一个抢到锁的人连续的acquire和release
    每acquire一次锁身上的计数加1
    每release一次锁身上的计数减1
    只要锁的计数不为0 其他人都不能抢

    from threading import Thread,Lock
    import time
    
    mutexA = mutexB = RLock()  # A B现在是同一把锁
    
    
    class MyThread(Thread):
        def run(self):  # 创建线程自动触发run方法 run方法内调用func1 func2相当于也是自动触发
            self.func1()
            self.func2()
    
        def func1(self):
            mutexA.acquire()
            print('%s抢到了A锁'%self.name)  # self.name等价于current_thread().name
            mutexB.acquire()
            print('%s抢到了B锁'%self.name)
            mutexB.release()
            print('%s释放了B锁'%self.name)
            mutexA.release()
            print('%s释放了A锁'%self.name)
    
        def func2(self):
            mutexB.acquire()
            print('%s抢到了B锁'%self.name)
            time.sleep(1)
            mutexA.acquire()
            print('%s抢到了A锁' % self.name)
            mutexA.release()
            print('%s释放了A锁' % self.name)
            mutexB.release()
            print('%s释放了B锁' % self.name)
    
    for i in range(10):
        t = MyThread()
        t.start()
    
    

    5.GIL全局解释器锁

    ps: python解释器有很多种 最常见的就是Cpython解释器
    GIL本质也是一把互斥锁:将并发变成串行牺牲效率保证数据的安全
    用来阻止同一个进程下的多个线程的同时执行(同一个进程内多个线程无法实现并行但是可以实现并发)
    python的多线程没法利用多核优势 是不是就是没有用了?

    GIL的存在是因为CPython解释器的内存管理不是线程安全的

    垃圾回收机制(内存管理)
    1.引用计数 : 值与变量绑定关系的个数 (一个变量有几个名字,名称空间)
    2.标记清除 : 当内存快要满的时候 ,会自动停止程序的运行 从上往下,检测所有的变量与值的绑定关系,给没有绑定关系的值打上标记, 最后一次性清楚 (python内部机制,自动触发)
    3.分代回收 :垃圾回收机制也是一块功能,是功能就需要消耗。程序内部有一部分类似与常量,减少垃圾回收的消耗,应该对变量与值的绑定关系做一个分类。新生代>>>青春代>>>老年代 ,垃圾回收机制扫描一档次数发现关系还在,会将该对关系移至下一代,随着待数的递增,扫描频率是降低的

    同一个进程下能否多个线程同时运行?:不能

    GIL就相当于加在解释器上的锁,多线程同时抢这把锁,谁先抢到谁先执行:

    1565831514212

    研究python的多线程是否有用需要分情况讨论
    四个任务 计算密集型的 10s
    单核情况下
    开线程更省资源
    多核情况下
    开进程 10s
    开线程 40s

    四个任务 IO密集型的
    单核情况下
    开线程更节省资源
    多核情况下
    开线程更节省资源

    例子:计算密集型

    from multiprocessing import Process
    from threading import Thread
    import os,time
    def work():
        res=0
        for i in range(100000000):
            res*=i
    
    
    if __name__ == '__main__':
        l=[]
        print(os.cpu_count())  # 本机为6核
        start=time.time()
        for i in range(6):
            # p=Process(target=work) #耗时  4.732933044433594
            p=Thread(target=work) #耗时 22.83087730407715
            l.append(p)
            p.start()
        for p in l:
            p.join()
        stop=time.time()
        print('r
    
    

    例子:IO密集型

    from multiprocessing import Process
    from threading import Thread
    import threading
    import os,time
    def work():
        time.sleep(2)
    
    
    if __name__ == '__main__':
        l=[]
        print(os.cpu_count()) #本机为6核
        start=time.time()
        for i in range(40):
            p=Process(target=work) #耗时9.001083612442017s多,大部分时间耗费在创建进程上
            # p=Thread(target=work) #耗时2.051966667175293s多
            l.append(p)
            p.start()
        for p in l:
            p.join()
        stop=time.time()
        print('run time is %s' %(stop-start))
    
    

    6.信号量锁

    模块 from threading import Semaphore

    信号量在不同的领域中,对应不同的知识点

    互斥锁:一个厕所一个坑,

    信号量:一个厕所多个坑,必须等有空位置,下一个人才能进去。一次可容纳多个人

    例子:40个人排队上5个坑位的公共厕所

    from threading import Semaphore,Thread
    import time
    import random
    
    
    sm = Semaphore(5)  # 造了一个含有五个的坑位的公共厕所
    
    def task(name):
        sm.acquire()
        print('%s占了一个坑位'%name)
        time.sleep(random.randint(1,3))
        sm.release()
    
    for i in range(40):   # 40人
        t = Thread(target=task,args=(i,))  # 生成线程对象
        t.start()  # 启动
    
    

    三、进程与进程的通讯

    queue:队列模块

    joinablequeue:可等待的队列模块

    队列:先进先出(买包子,先排队先买)

    堆栈:先进后出(衣柜放衣服,先放进去,后拿出来)

    1.队列中的基本方法

    from multiprocessing import Queue

    q = Queue(5) # 生成队列对象,括号内的数字表示 这个队列最大储存数
    q.put(1) # 往队列中添加数据1,
    # 当队列满了,再放数据,不会报错,程序原地等待(阻塞态),等待有位置放值

    q.get() # 取值,因为先进先出,所以先取第一个值
    # 当队列取完之后,再次取值,程序原地等待(阻塞态),等待有值取
    q.get_nowait() # 取值不等待,没有了直接报错

    q.full() # 判断队列是否满了(bool值)
    q.empty() # 判断队列是否为空(bool值)

    q.join() # 等待

    2.队列、堆栈、优先级队列

    队列:先进先出 q = queue.Queue()

    堆栈:先进后出 q = queue.LifoQueue()

    优先级队列:数字越小,优先级越高 q = queue.PriorityQueue()

    1.队列生成

    q = queue.Queue()
    q.put('hahha')
    print(q.get())
    结果:hahha
    
    

    2.堆栈生成

    q = queue.LifoQueue()
    q.put(1)
    q.put(2)
    q.put(3)
    print(q.get())
    结果:  3   先进后出
    
    

    3.优先级队列生成

    q = queue.PriorityQueue()
    # 数字越小 优先级越高
    q.put((10,'haha'))
    q.put((100,'hehehe'))
    q.put((0,'xxxx'))
    q.put((-10,'yyyy'))
    print(q.get())
    结果:(-10, 'yyyy')  
    
    

    3.队列之间的通讯

    from multiprocessing import Process,Queue
    
    """
    创建两个子进程,让一个子进程放数据,另一个子进程拿数据
    """
    
    
    def producer(q):
        q.put('hello jeff')
    
    
    def consumer(q):
        print(q.get())
    
    
    if __name__ == '__main__':
        q = Queue()  # 生成一个队列
        p = Process(target=producer, args=(q,))  # 创建一个生产进程对象
        c = Process(target=consumer, args=(q,))  # 创建一个消费进程对象
        p.start()  # 创建进程,启动
        c.start()  # 创建进程,启动
    
    
    
    
    

    4.生产者与消费者模型

    """
    生产者:生产/制造数据的
    消费者:消费/处理数据的
    例子:做包子的,买包子的
    1.做包子远比买包子的多
    2.做包子的远比包子的少
    供需不平衡的问题
    """

    from multiprocessing import Process,Queue,JoinableQueue
    import random
    import time
    
    
    def producer(name, food, q):
        for i in range(5):
            data = '%s生产了%s%s'%(name,food,i)
            time.sleep(random.random())  # 等待0-1秒
            q.put(data)  # 往队列里放
            print(data)
    
    def consumer(name,q):
        while True:
            data = q.get()  # 从队列里取
            if data == None:break
            print('%s吃了%s'%(name,data))
            time.sleep(random.random())
            q.task_done()  # 告诉队列你已经从队列中取出了一个数据 并且处理完毕了
    
    
    if __name__ == '__main__':
        q = JoinableQueue()
    
        p = Process(target=producer, args=('辜友银','奥利奥',q))
        p1 = Process(target=producer, args=('cherry','狮子头',q))
        c = Process(target=consumer, args=('陈嘉旻',q))
        c1 = Process(target=consumer, args=('jeff',q))
        p.start()
        p1.start()
        c.daemon = True  # 设置守护进程
        c1.daemon = True
        c.start()
        c1.start()
        p.join()
        p1.join()
    
        q.join()  # 等到队列中数据全部取出
    
    

    四、线程

    1.线程简介

    1.什么是线程

    内存是工厂,进程是车间,线程是流水线

    进程线程其实都是虚拟单位,都是用来帮助我们更加形象的迷哦书某种事物

    进程:资源单位

    线程:执行单位

    ​ ps:每个进程都自带一个线程,线程才是真正的执行单位,进程只是再线程中提供资源(车间提供资源给流水线)

    2.为什么要有线程

    开进程:1.申请内存空间 耗资源

    ​ 2.进程从上至下执行代码,‘拷贝代码’ 耗资源

    **开线程:1.一个进程内可以开多个线程,并且线程之间的数据是共享的。(一个车间内可以有多个流水线,但是一个车间的资源都是共享的)

    ​ ps:开启线程的开销远远小于开启进程的开销

    3.同一个进程下的多个线程本来就是数据共享,为什么还要用队列?

    因为队列是 管道+锁 使用队列你就不需要自己手动操作锁的问题

    锁操作的不好极容易产生死锁现象

    2.创建线程的两种方式

    关键字:from threading import Thread 线程模块

    注意:与创建进程的两种方式一样

    1.第一种第一种实例化Thread方法

    # 第一种第一种实例化Thread方法
    def task(name):
        print('%s 开启了' % name)
        time.sleep(3)
        print('%s 结束了' % name)
    
    
    if __name__ == '__main__':
        t = Thread(target=task, args=('jeff',))  # 创建子线程对象
        t.start()  # 告诉操作系统开启这个线程
        print('主线程')
    
    

    2.第二种,继承Thread类重写run方法

    # 第二种,继承Thread类重写run方法
    from threading import Thread
    import time
    
    class MyThread(Thread):
        def __init__(self, name):
            super().__init__()
            self.name = name
    
        def run(self):
            print('%s 开启了' % self.name)
            time.sleep(3)
            print('%s 关闭了' % self.name)
    
    t = MyThread('jeff')
    t.start()
    print('主线程')
    
    

    3.线程之间的资源是共享的

    一个进程里面可以有很多线程,一个进程中的资源都是共享的。一个车间可以有很多流水线,都是用的同一个车间的资源。

    4.守护线程

    关键字:子线程对线.daemon = True 设置该 子线程 为 主线程的 守护线程

    主线程结束,守护线程立马结束

    注意:主线程结束,意味着进程结束,资源销毁;但是其他非守护线程还没有结束,还在使用资源,所以应该等待其他非守护线程结束之后,主线程才结束,进程才结束

    from threading import Thread,current_thread
    import time
    
    def task(i):
        print(current_thread().name)   # 打印子线程的名字
        time.sleep(i)
        print('子线程结束')
    t = Thread(target=task,args=(1,))
    t.daemon = True   # 设置为守护线程
    t.start()  # 启动
    print('主线程')
    
    

    5.线程之间的通讯

    from threading import Thread
    
    money = 100
    
    
    def task():
        global money  # 申明全局变量
        money = 999   # 修改全局
    
    t = Thread(target=task)  # 创建线程对象
    t.start()  # 启动
    t.join()  # 等待t子线程执行结束
    print(money)  # 修改成功
    #结果:999
    
    

    6.event事件

    模块: from threading import Event

    e = Event() # 生成一个Event对象

    e.set() # 发信号

    e.wait # 等待信号

    例子:等待红绿灯

    from threading import Event,Thread
    import time
    
    # 先生成一个event对象
    e = Event()
    
    
    def light():
        print('红灯正亮着')
        time.sleep(3)
        e.set()  # 发信号
        print('绿灯亮了')
    
    def car(name):
        print('%s正在等红灯'%name)
        e.wait()  # 等待接收信号,信号接收到立马执行下一步
        print('%s加油门飙车了'%name)
    
    t = Thread(target=light)
    t.start()
    
    for i in range(10):
        t = Thread(target=car,args=('小明%s'%i,))
        t.start()
    
    

    五、简单的多线程案列

    客户端:

    import socket
    
    
    client = socket.socket()
    client.connect(('127.0.0.1',8080))
    
    while True:
        client.send(b'hello')
        data = client.recv(1024)
        print(data.decode('utf-8'))
    
    

    服务端:

    import socket
    from threading import Thread
    
    """
    服务端
        1.要有固定的IP和PORT
        2.24小时不间断提供服务
        3.能够支持并发
    """
    
    server = socket.socket()
    server.bind(('127.0.0.1',8080))
    server.listen(5)
    
    
    # 封装的通信功能
    def talk(conn):
        while True:
            try:
                data = conn.recv(1024)
                if len(data) == 0:break
                print(data.decode('utf-8'))
                conn.send(data.upper())
            except ConnectionResetError as e:
                print(e)
                break
        conn.close()
    
    
    while True:
        conn, addr = server.accept()  # 监听 等待客户端的连接  阻塞态
        print(addr)
        t = Thread(target=talk,args=(conn,))
        t.start()
    
    

    六、进程池与线程池

    开线程的资源虽然少,但是架不住量大

    在计算机能够承受范围之内最大限度的利用计算机

    1.什么是池?

    ​ 在包装计算机硬件安全的情况下最大限度的利用计算机

    ​ 池其实降低的程序的运行效率,但是保证了硬件的安全

    ​ 因为硬件的发展跟不上软件的发展

    2.线程池进程池?

    提交任务的方式?

    1.同步:提交任务后,等待返回结果 期间不做任何事

    2.异步:提交任务后,不等待返回结果(异步回调),直接执行下一行代码

    异步提交:池.submit(函数名,参数)

    异步回调:n.result() 当异步提交的任务有返回结果之后,会自动触发回调函数的执行

    ​ 注意:这里的n代表的是提交函数的返回结果

    # 提交任务的时候,绑定一个回调函数  一旦该任务有结果 立刻执行对应的回调函数
    
    
    pool = ProcessPoolExecutor()  # 创建进程池    默认当前计算机的cpu的个数
    pool = ThreadPoolExecutor(5)  # 创建线程池    括号内可以传参数指定线程池的线程的个数,不传默认cpu个数*5
    
    pool.submit(task, i)    # 朝池中提交任务   异步提交
    
    
    print('拿到了异步提交的返回结果',n.result())
    
    

    1565836614828

    from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
    import os
    import time
    
    
    def task(n):
        print(n,os.getpid())  # 查看当前进程号
        time.sleep(1)
        return n+100
    
    def call_back(n):
        print('拿到了异步提交的返回结果',n.result())
    
    
    os.cpu_count()  # 查看当前计算机的cup数量
    pool = ProcessPoolExecutor()  # 默认当前计算机的cpu的个数
    # pool = ThreadPoolExecutor(5)  # 括号内可以传参数指定线程池的线程的个数,不传默认cpu个数*5
    
    
    if __name__ == '__main__':
        t_list = []
        for i in range(10):
            # 提交任务的时候,绑定一个回调函数  一旦该任务有结果 立刻执行对应的回调函数
            res = pool.submit(task, i).add_done_callback(call_back)
            t_list.append(res)
    
    
    
    

    七.协程

    单线程下实行并发

    进程:资源单位

    线程:执行单位

    协程:单线程下实行并发,(欺骗操作系统,让操作系统误认为一直没有IO,运行态和就绪态切换)

    ​ 切换+保存状态不一定提升效率?

    ​ 1.IO密集型 提升效率

    ​ 2.计算密集型 降低效率

    ​ 并发本质:切换+保存状态

    ​ ps:看起来像同时执行,称之并发

    协程:完全是程序员自己想出来的名词,单线程下实现并发

    并发的条件?

    ​ 多道技术

    ​ 空间上的复用

    ​ 时间上的复用:切换——保存状态(IO操作)

    1.协程的创建

    检测有没有IO的模块:genent 模块

    检测所有的任务,来回切换:spawn 配合join来使用

    注意:gevent模块没办法自动识别time.sleep,等io情况,需要手动在配置一个参数,monkey.patch_all() 1565839275845

    from gevent import monkey;monkey.patch_all()  # 由于该模块经常被使用 所以建议写成一行
    from gevent import spawn
    import time
    
    def heng():
        print('哼')
        time.sleep(1)
        print('哼')
    
    def ha():
        print('哈')
        time.sleep(3)
        print('哈')
    
    def hei():
        print('嘿')
        time.sleep(5)
        print('嘿')
    
    # 异步    5.011743068695068
    start = time.time()
    a1 = spawn(heng)  # spawn会检测所有的任务,来回切换,实际只有一个线程来回的切换完成所有的任务
    a2 = spawn(ha)
    a3 = spawn(hei)
    a1.join()
    a2.join()
    a3.join()
    print(time.time()-start)
    
    
    
    # 同步  9.013060092926025
    # start = time.time()
    # a1 = heng()
    # a2 = ha()
    # a3 = hei()
    # print(time.time()-start)
    
    

    2.TCP单线程实现并发

    客户端:

    import socket
    from threading import Thread,current_thread
    
    
    def client():
        client = socket.socket()
        client.connect(('127.0.0.1', 8080))
        n = 0
        while True:
    
            data = '%s %s' % (current_thread().name,n)
            client.send(data.encode('utf-8'))
            res = client.recv(1024)
            print(res.decode('utf-8'))
            n += 1
    
    for i in range(10):
        t = Thread(target=client)
        t.start()
    
    
    
    

    服务端:

    from gevent import monkey;monkey.patch_all()
    import socket
    from gevent import spawn
    
    server = socket.socket()
    server.bind(('127.0.0.1', 8080))
    server.listen(5)
    
    
    def talk(conn):
        while True:
            try:
                data = conn.recv(1024)
                print(data.decode('utf-8'))
                conn.send(data.upper())
            except BaseException as f:
                print(f)
                break
        conn.close()
    
    
    def server1():
        while True:
            conn, addr = server.accept()
            spawn(talk,conn)
    
    
    if __name__ == '__main__':
        a1 = spawn(server1)     # spawn会检测所有的任务,来回切换,配合join使用
        a1.join()
    
    
  • 相关阅读:
    描述JSP和Servlet的区别、共同点、各自应用的范围
    JS中的三种弹出式消息提醒(警告窗口、确认窗口、信息输入窗口)的命令是什么?
    如何优化数据库,如何提高数据库的性能?
    什么是数据库的参照完整性?
    常用的设计模式有哪些?说明工厂模式。
    struts2中,Action通过什么方式获得用户从页面输入的数据,又是通过什么方式把其自身的数据传给视图的?
    struts2中,OGNL访问值栈的时候查找的顺序是什么?请排序:模型对象、临时对象、固定名称的对象、Action对象
    Struts2包含哪些标签?
    说出几个与spring同类型的开源框架,说出几个与hibernate同类型的开源框架,说出几个与struts同类型的开源框架
    Log4J是Apache组织的开源一个开源项目,通过Log4J,可以指定日志信息输出的目的地,如console、file等。Log4J采用日志级别机制,请按照输出级别由低到高的顺序写出日志输出级别。
  • 原文地址:https://www.cnblogs.com/WQ577098649/p/11887468.html
Copyright © 2011-2022 走看看