• 并发编程---进程


    一,背景知识

    进程即正在执行的一个过程。进程是对正在运行程序的一个抽象。

    进程的概念起源于操作系统,是操作系统最核心的概念,也是操作系统提供的最古老也是最重要的抽象概念之一。操作系统的其他所有内容都是围绕进程的概念展开的。

    所以想要真正了解进程,必须事先了解操作系统,点击进入    

    PS:即使可以利用的cpu只有一个(早期的计算机确实如此),也能保证支持(伪)并发的能力。将一个单独的cpu变成多个虚拟的cpu(多道技术:

    时间多路复用和空间多路复用+硬件上支持隔离),没有进程的抽象,现代计算机将不复存在。

    理论基础

    #一 操作系统的作用:
        1:隐藏丑陋复杂的硬件接口,提供良好的抽象接口
        2:管理、调度进程,并且将多个进程对硬件的竞争变得有序
    
    #二 多道技术:
        1.产生背景:针对单核,实现并发
        ps:
        现在的主机一般是多核,那么每个核都会利用多道技术
        有4个cpu,运行于cpu1的某个程序遇到io阻塞,会等到io结束再重新调度,会被调度到4个
        cpu中的任意一个,具体由操作系统调度算法决定。
        
        2.空间上的复用:如内存中同时有多道程序
        3.时间上的复用:复用一个cpu的时间片
           强调:遇到io切,占用cpu时间过长也切,核心在于切之前将进程的状态保存下来,这样
                才能保证下次切换回来时,能基于上次切走的位置继续运行

    二,进程

    进程:正在进行的一个过程或者说一个任务。而负责执行任务则是cpu。

    1,并发与并行

    无论是并行还是并发,在用户看来都是'同时'运行的,不管是进程还是线程,都只是一个任务而已,真是干活的是cpu,cpu来做这些任务,而一个cpu同一时刻只能执行一个任务

    (1),并发:并发是伪并行,即看起来是同时运行。单个cpu+多道技术就可以实现并发,(并行也属于并发)

    (2) 并行:同时运行,只有具备多个cpu才能实现并行

      

    所有现代计算机经常会在同一时间做很多件事,一个用户的PC(无论是单cpu还是多cpu),都可以同时运行多个任务(一个任务可以理解为一个进程)。

        启动一个进程来杀毒(360软件)

        启动一个进程来看电影(暴风影音)

        启动一个进程来聊天(腾讯QQ)

    所有的这些进程都需被管理,于是一个支持多进程的多道程序系统是至关重要的

    多道技术概念回顾:内存中同时存入多道(多个)程序,cpu从一个进程快速切换到另外一个,使每个进程各自运行几十或几百毫秒,这样,虽然在某一个瞬间,一个cpu只能执行一个任务,但在1秒内,cpu却可以运行多个进程,这就给人产生了并行的错觉,即伪并发,以此来区分多处理器操作系统的真正硬件并行(多个cpu共享同一个物理内存)

     进程间的数据不会共享

    data_list = []
    
    def task(arg):
        data_list.append(arg)
        print(data_list)
    
    
    def run():
        for i in range(5):
            p = Process(target=task,args=(i,))
            p.start()
    
    if __name__ == '__main__':
        run()

    打印结果

    [0]
    [4]
    [2]
    [1]
    [3]

    通过类创建进程

    # 通过继承类来创建进程
    class Myprocess(Process):
    
        def run(self):
            print(f"当前进程{multiprocessing.current_process()}")
    
    
    if __name__ == '__main__':
        for i in range(4):
            p = Myprocess()
            p.start()

    如果要启动大量的子进程,可以用进程池的方式批量创建子进程:

    from multiprocessing import Pool
    import os
    
    def func(i):
        print(f"进程{i} id>>>{os.getpid()}")
    
    
    if __name__ == '__main__':
        p = Pool(5)
        for i in range(6):
            p.apply_async(func, args=(i,))
        p.close()
        p.join()  # join()方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步。
       print("end")

    进程间通信

    Process之间肯定是需要通信的,操作系统提供了很多机制来实现进程间的通信。Python的multiprocessing模块包装了底层的机制,提供了QueuePipes等多种方式来交换数据。

     我们以Queue为例,在父进程中创建两个子进程,一个往Queue里写数据,一个从Queue里读数据:

    from multiprocessing import Process, Queue
    import os,time,random
    
    # 写数据进程执行的代码
    def write(q):
        print("Process to write: %s" % os.getpid())
        for value in ['A',"B",'C']:
            print(f"Put {value} to queue...")
            q.put(value)
            time.sleep(random.random())
    
    
    # 读数据进程执行代码
    def read(q):
        print(f"Process to read: {os.getpid()}")
        while 1:
          value = q.get(1)
          print(f'Get {value} from queue.')
    
    
    if __name__ == '__main__':
        # 父进程创建Queue,并传给各个子进程
        q = Queue()
        pw = Process(target=write, args=(q,))
        pr = Process(target=read, args=(q,))
        # 启动子进程write,写入
        pw.start()
        # 启动子进程pr读取
        pr.start()
        # 等待pw的结果
        pw.join()
        # pr进程里是死循环,无法等待其结束,只能强行终止:
        pr.terminate()

    Manager进程间数据共享

    from multiprocessing import Process, Manager
    
    def task(arg, dic):
        time.sleep(0.5)
        dic[arg] = 100
    
    
    if __name__ == '__main__':
        m = Manager()
        dic = m.dict()
    
        process_list = []
        for i in range(10):
            p = Process(target=task, args=(i, dic,))
            p.start()
            # p.join()
            process_list.append(p)
    
        while True:
            count = 0
            for p in process_list:
                if not p.is_alive():  # is_alive 看进程是否还存活着
                    count += 1
            if count == len(process_list):
                break
        print(dic)
    # ##################### 进程间的数据通信,其他电脑 #####################
    def task(url, dic):
        '''
        假设写了个爬虫,爬取的数据url对应的数据
        :param url: 
        :param dic: 
        :return: 
        '''
        dic[url] = "res..."
        pass
    
    if __name__ == '__main__':
        while True:
            # 连接上指定的服务器
            # 去机器上获取url
            url = 'adfasdf'
            p = Process(target=task, args=(url,))
            p.start()

    进程锁

    进程锁与线程锁的用法一模一样

    为什么要加锁,用到锁是因为数据共享,数据不安全,而进程默认是数据隔离的;当不同的进程用到相同的数据时(进程间通信,数据共享),才用到进程锁。

    线程加锁是因为同一进程下的多线程数据是共享的,线程不安全(多个线程改同一个变量把内容改乱了),加锁是为了线程安全。

    import multiprocessing
    import time
    
    lock = multiprocessing.Lock()
    
    
    def task(arg):
        print("进程来了")
        lock.acquire()
        time.sleep(2)      
        print(arg)
        lock.release()
    
    if __name__ == '__main__':
        p1 = multiprocessing.Process(target=task, args=(1,))
        p1.start()
        p2 = multiprocessing.Process(target=task, args=(2,))
        p2.start()
  • 相关阅读:
    python笔记26(正则表达式、re模块)
    python笔记24(回顾、复习)
    python笔记25(正则表达式课程一)
    python笔记23(面向对象课程五)
    python(leetcode)-1.两数之和
    python(leetcode)-283移动零
    python(leetcode)-66加一问题
    python(leetcode)-350两个数组的交集
    python(leetcode)-136只出现一次的数字
    python数据结构-数组/列表/栈/队列及实现
  • 原文地址:https://www.cnblogs.com/zwq-/p/9620511.html
Copyright © 2020-2023  润新知