• 进程-线程-协程


    一、线程锁

    互斥锁

    1、互斥锁:解决资源争用,数据读取不一致等问题

    import threading
    import time
    num = 0
    def sum_num(i):
        lock.acquire()
        global num
        time.sleep(1)
        num +=i
        print(num)
        lock.release()
    t_list = []
    
    lock = threading.Lock()  #创建一个互斥锁对象
    for i in range(6):
        t = threading.Thread(target=sum_num,args=(i,))
        t_list.append(t)
        t.start()
    [t.join() for t in t_list]
    print("end.................")
    
    
    def main():
        t_list = []
        for i in range(5):
            # get_content("https://www.baidu.com")
            #创建线程
            #target  -->指定传入的方法的名字 , 要做什么
            #args  --》 指定方法需要传入的参数  元组类型
            t = threading.Thread(target = get_content, args=("https://www.baidu.com",))
            t_list.append(t)
            #默认是前台线程,主线程执行完了,等子线程执行完再退出
            # t.setDaemon(True) #设置后台线程,主线程退出子线程也退出
            t.start()  #启动线程  #--》自动执行run方法
    
        for t in t_list:
            #阻塞当前环境上下文,直到为t的线程执行完成
            #谁执行这个join代码,谁就是当前环境
            t.join()
            # t.join(timeout=0.3)
    
    main()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    2、原始锁与重入锁(Lock vs RLock)

    # ​Lock:原始锁  --》获取锁之前不做判断,直到获取到锁位为止
    # ​RLock:重入锁  --》获取锁之前先判断,如果自己有了锁,就立即返回 
    
    import threading
    lock1 = threading.Lock()
    lock2 = threading.RLock()
    
    # lock1.acquire()
    # print("lock1 acquire 1")
    # lock1.acquire() #同一个线程,获得原始锁之后,没有释放又去尝试获取原始锁,就会产生
    lock2.acquire()
    print("lock2 acquire 1")
    lock2.acquire()
    print("lock2 acquire 2")
    lock2.release()
    print("lock2 release ....1")
    lock2.release()
    print("lock2 release....2")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    3、死锁

    #1、第一种情况
    # import threading
    # # lock1 = threading.Lock()
    # lock1 = threading.RLock()
    # lock1.acquire()
    # print("lock1 acquire 1")
    # lock1.acquire() #同一个线程,获得原始锁之后,没有释放又去尝试获取原始锁,就会产生死锁
    # print("lock1 acquire 2")
    # lock1.release()
    # print("lock1 release ....1")
    # lock1.release()
    # print("lock1 release....2")
    
    #第2种情况
    from threading import Thread,Lock
    import time
    class Account:
        def __init__(self,_id,balance,lock):
            self._id = _id
            self.balance = balance
            self.lock = lock  #每个账户自带一个锁,只要对balance变态,就提前锁住
        #取钱
        def withdraw(self,amount):
            self.balance -= amount
        #存钱
        def deposite(self,amount):
            self.balance += amount
        #查看余额
        def get_balance(self):
            return self.balance
    Liu = Account("liuhongjie",50000,Lock())
    zheng = Account("zhengzheng",10000,Lock())
    #转账函数,谁的账户金额要动,需要先上锁
    #from为转出账户,to为转入账户,amount为金额
    def transfer(from_,to_,amount):
        #from_账户上锁
        if from_.lock.acquire():
            from_.withdraw(amount)  #from_账户减少
            time.sleep(1)
            print(f"{from_._id} 向{to_._id} 转了{amount}元")
            if to_.lock.acquire():
                to_.deposite(amount) #to_账户增加
                to_.lock.release() #to_账户加钱完毕,解锁
            from_.lock.release() #from——账户转账完毕解锁
        print(f"{from_._id} 向{to_._id} 转了{amount}元")
    
    # transfer(Liu,zheng,5000)
    # print(Liu.get_balance())
    t1 = Thread(target=transfer,args=(Liu,zheng,4000)) #刘洪杰给小郑转了4000
    t2 = Thread(target=transfer,args=(zheng,Liu,1000)) #小郑给刘洪杰转了1000
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print(Liu.balance)
    print(zheng.balance)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    4、避免产生死锁:

    1.尽量避免同一个线程对多个Lock进行锁定
    2.多个线程需要对多个Lock进行锁定,尽量保证他们以相同的顺序加锁
    3.设置超时
    
    • 1
    • 2
    • 3
    其他类锁
    互斥锁 -- 允许一个线程执行
    信号锁 -- 允许n个线程执行
    事件锁 -- 条件变量  ,满足条件,全部线程都执行
    条件锁 -- 信号量+事件锁,满足条件,允许n个线程访问执行
    
    linux里的线程通信:互斥锁+信号量+条件变量
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    二、全局解释器锁 – GIL

    1、GIL:全局解释器锁,是因为历史遗留问题存在cpython中(Jpython无此类问题)
    2、作用:保证同一个进程内,同一时刻只有一个线程能执行代码,只有当io阻塞或者时间片用完时,才会释放这个GIL
    3、由于GIL锁的限制,所以多线程不适合计算型任务,而更适合IO型任务,比如:网络IO(抓取网页数据)、磁盘操作(读写文件)、键盘输入
    4、多进程更适合 计算密集型任务:用CPU、计算

    三、操作系统

    1、计算机组成:cpu+存储+I/O
    2、资源:计算资源和存储资源
    3、linux操作系统–》五大子系统

    1.进程调度
    	调度算法:
    		先进先出
    		短进程优先
    		优先级
    2.内存管理
    	虚拟内存、虚拟地址映射、段页机制、缺页中断、内存的分配管理、伙伴系统
    3.文件系统
    	虚拟文件系统、ext系列系统、xfs系统
    4.网络接口
    5.进程通信
    	前提:
    		进程间是相互独立的
    		进程就是正在运行的程序,是计算机进行资源分配的最小单位
    	方式:
    		管道(队列)
    			匿名管道:父子进程之间才能通信
    			命名管道:不是父子进程也能通信
    		信号
    			异步通信
    			发送信号
    				硬件发送
    					ctrl + c
    				软件发送
    					kill
    		信号量(与共享内存配合使用)
    			相当于一把锁,可以规定同一时刻有多少程序能够访问共享内存
    		共享内存
    			进程间最快的通信方式
    		socket
    			不同主机上面的不同进程通信,也可以用在同一主机不同进程的通信
    		消息队列
    			支持传输的类型多一点
    			内核启动就创建好了的
    			存储空间比较小		
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    4、cpu两种状态

    ​	用户态(运行普通程序)
    ​	内核态(运行内核程序)
    
    • 1
    • 2

    5、进程的组成

    PCB(进程控制块,是进程的唯一标识)+数据段+代码段
    是计算机正在运行的程序,是计算机资源分配的最小单位
    
    • 1
    • 2

    6、线程

    线程运行在进程之上,是操作系统进行调度的最小单位
    
    • 1

    7、进程和线程的关系

    1、一个进程里可以有一个以上的线程,这些线程都是共享进程里的内存空间的
    2、不同进程之间内存空间都是独立的
    3、创建新的线程很简单,创建一个新的进程需要对其父进程进行一次克隆
    4、一个线程可以控制和操作同一个进程里的其他线程,进程只能操作子进程
    5、一个主线程改变,可能会影响其他线程,改变父进程不会影响子进程
    
    • 1
    • 2
    • 3
    • 4
    • 5

    8、高并发编程

    多线程
    多进程
    多进程+多线程
    
    • 1
    • 2
    • 3

    9、并发和并行:

    并发:交替执行(某段时间内的处理能力)
    并行:同时执行
    
    • 1
    • 2

    10、多进程 VS 多进程

    1.一般来说多线程开销会比多进程少
    2.上下文切换
    
    • 1
    • 2

    11、进程的三态模型

    就绪、运行、阻塞
    
    • 1

    12、linux里的五种状态:

    运行R、中断S、不可中断D、僵尸Z、停止T
    
    • 1

    13、孤儿进程与僵尸进程

    1.孤儿进程:
    父进程退出,子进程还在运行,那么这个子进程就会成为孤儿进程,孤儿进程会被pid为1的进程所收养
    2.僵尸进程:
    子进程退出,父进程没有响应,父进程没有去调用wait()或者waitpid()去获取子进程的状态,子进程的进程控制块就会依然保存在系统中,这种进程称为僵尸进程
    
    • 1
    • 2
    • 3
    • 4

    四、多线程

    1、threading模块

    import requests
    import functools
    import time
    import threading
    
    def runtime(func):
        #保留传递进来的函数的元数据,将它的元数据赋值给inner
        @functools.wraps(func)
        def inner(*args, **kwargs):  #让装饰器更加通用
            start = time.time()
            result = func(*args, **kwargs)
            end = time.time()
            print(f"函数执行花了{end -start}s")
            return result
        return inner
    
    def get_content(url):
        text = requests.get(url).content
        time.sleep(0.5)
        print("get content")
    
    @runtime
    def main():
        t_list = []
        for i in range(5):
            # get_content("https://www.baidu.com")
            #创建线程
            #target  -->指定传入的方法的名字 , 要做什么
            #args  --》 指定方法需要传入的参数  元组类型
            t = threading.Thread(target = get_content, args=("https://www.baidu.com",))
            t_list.append(t)
            #默认是前台线程,主线程执行完了,等子线程执行完再退出
            # t.setDaemon(True) #设置后台线程,主线程退出子线程也退出
            t.start()  #启动线程  #--》自动执行run方法
    
        for t in t_list:
            #阻塞当前环境上下文,直到为t的线程执行完成
            #谁执行这个join代码,谁就是当前环境
            t.join()
            # t.join(timeout=0.3)
    main()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    2、自定义线程类创建

    import requests
    import threading
    import time
    class MyThread(threading.Thread):
        def __init__(self,num):
            super(MyThread,self).__init__()
            self.num = num
        def run(self):
            print(f"runing on numbers:{self.num}")
    
    def get_content(url):
        text = requests.get(url).content
        time.sleep(0.5)
        print("get content")
    
    t1 = MyThread(get_content("www.baidu.com"))
    t2 = MyThread(2)
    t1.start()
    t2.start()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    五、多进程

    1、多进程-fork

    import os,time
    print("==================start==========")
    pid = os.fork()
    #父进程运行时,得到的pid为子进程的pid,子进程运行时这个pid就是0
    print("outerside pid is :",pid)
    if pid == 0:
        print("child process")
        time.sleep(60)
        print("child pid is:",os.getpid())
        print("child-parent pid is :",os.getppid())
    else:
        print("parent process")
        time.sleep(60)
        print("parent pid is ",os.getpid())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2、多进程-multiprocessing

    #各个进程都有一份独立的数据,相互隔离
    from multiprocessing import Process,current_process
    import time
    lst = []
    def task(i):
        print(current_process().name,i,'start............')
        time.sleep(2)
        lst.append(i)
        print(lst)
        print(current_process().name,i,'end...........')
    
    if __name__ == "__main__":  # 直接运行的模块下才会相等,而不是导入模块
        p_lst = []
        for i in range(4):
            p = Process(target= task,args= (i,))
            p_lst.append(p)
            p.start()
        for p in p_lst:
            p.join()
        print("main end.......")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    3、自定义进程类创建多进程

    from multiprocessing import Process
    import time
    class Myprocess(Process):
        def __init__(self,num):
            super(Myprocess,self).__init__()
            self.num = num
        def run(self):
            print(f"runing on numbers:{self.num}")
    
    if __name__=="__main__":
        t1 = Myprocess(1)
        t2 = Myprocess(2)
        t1.start()
        t2.start()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    4、多进程数据共享

    4.1、使用Manager实现数据共享,必须确保是在当前模块执行,而不是模块调用执行
    from multiprocessing import Manager,Process,Lock
    import time
    
    def func(i,temp):
        with lock:
            time.sleep(1)
            temp[0] += 100
            # time.sleep(1)
            print(i,"----------------->",temp[0])
    
    #使用manager,父进程要等待子进程结束再退出
    #用socker方式实现
    lock = Lock()
    if __name__ == "__main__":
        manager = Manager()
        temp = manager.list([1,2,3])
        p_list = []
        for i in range(10):
            p = Process(target=func,args=(i,temp))
            p.start()
            p_list.append(p)
        for i in p_list:
            i.join() #不加jion,manager进程会先退出,子进程就访问不到manager共享的数据了
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    4.2、使用队列实现数据共享
    from multiprocessing import Process,Queue
    import time
    def func(i,q):
        if not q.empty():
            print(i,"----->get value",q.get())
        time.sleep(2)
    # 先进先出
    if __name__ == "__main__":
        q = Queue()
        for i in range(6):
            q.put(10-i)
            p = Process(target=func,args=(i,q))
            p.start()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    六、协程

    1、协程拥有自己的寄存器上下文和栈,在执行函数A时,可以随时中断,去执行函数B,然后中断继续执行函数A(可以自由切换)。
    2、python对协程的支持:

    1.yield
        def func():
            i = 1
            while i <100:
                yield i
                i += 1
    
        result = func()
        print(next(result))
        print("xxxxxxxxxxxxxxxxxxxxxxxx")
        print(next(result))
    
    2.asyncio模块
        #异步编程
        import asyncio
    
        async  def func1():
            print(1)
            await asyncio.sleep(2)
            print(2)
    
        async def func2():
            print(3)
            await asyncio.sleep(2)
            print(4)
    
        #创建任务列表
        tasks = [
            asyncio.ensure_future(func1()),
            asyncio.ensure_future(func2())
        ]
    
        #生成事件循环
        loop = asyncio.get_event_loop()
        #运行
        loop.run_until_complete(asyncio.wait(tasks))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
  • 相关阅读:
    kali必杀器之三剑客
    typescript入门之helloworld
    单身狗1和单身狗2(C语言版)
    LeetCode 53.最大子数组和17.电话号码的字母组合
    d为何用模板参数
    第七天项目实战二
    【Java基础】Java基础知识
    Windows内核函数 - ANSI_STRING字符串与UNICODE_STRING字符串
    NLP基础——Bag of Words 词袋法
    nginx部署web项目(跟着搞不出来,来砍我)
  • 原文地址:https://blog.csdn.net/weixin_47661174/article/details/126295345