C++-Python_多进程_多线程-协程-异步开发

发布时间 2023-12-04 11:52:53作者: 辰令

python 多任务、并发编程等领域

 并发:宏观上并行,微观上串行
 并行:宏观上并行,微观上并行
 
并发:I/O密集型作业  运行态---阻塞态的转化
并行:CPU密集型作业 
    并发(Concurrent)、并行(Parallesim)、
    多线程(Multi Threaded)、多进程(Multiprocessing)、多任务(Multitasking) 、协程(Coroutine) 
    I/O密集型(I/O-bound)、CPU密集型(CPU-bound)
	同步(Synchronous) VS 异步(Asynchronous)
Python中有  线程、进程就是操作系统帮你实现的上下文切换技术,你无需关心进程调度
    协程对标的是线程,代替多线程-在用户态实现的上下文切换技术

  进程是操作系统中的一个基本概念,表示计算机中的一个独立执行单元。每个进程都拥有独立的内存空间、资源、数据等,可以独立于其他进程并行执行
   多进程编程的关键: 进程间的通信和同步,进程间的数据共享,以及进程间的异常处理
     多进程的实现方式主要有两种:一种是通过系统函数创建新进程,另一种是通过进程池实现。
进程 multiprocessing  :Process  Pipe Queue  Lock  Pool
    创建进程:通过调用 multiprocessing 模块中的 Process 类创建进程。
    启动进程:通过调用进程对象的 start() 方法启动进程。
    定义进程函数:创建一个函数,用于定义进程所执行的任务。
    使用队列进行进程间通信:通过使用 multiprocessing 模块中的队列,可以方便地实现进程间的通信。
    等待进程结束:通过调用进程对象的 join() 方法,可以等待进程结束。
    此外,multiprocessing 还提供了一些常用的同步机制,例如锁、条件变量等 
	  一个进程可以有多个线程,每条线程可以并发执行不同的任务
线程 threading
  Thread、Lock、Rlock、Condition、Semaphore、Event、Timer、local
   线程有五种状态:新建、就绪、运行、阻塞、死亡
concurrent.futures 模块,concurrent.futures是3.2引入的新库,
  在python的多线程threading、多进程multiprocesssing上进一步封装,实现了进程池和线程池
   concurrent.futures实现的都是异步操作
     它提供了ThreadPoolExecutor和ProcessPoolExecutor两个类,
   实现了对threading和multiprocessing的进一步抽象(这里主要关注线程池),不仅可以帮我们自动调度线程
   实现了对threading和multiprocessing的进一步抽象
asyncio 适合做I/O密集型任务,你看asyncio翻译过来就是异步I/O,讲的就是I/O密集任务。
	协程 async、await关键字 yield from  yield关键字
     yield是控制流程工具,
	 yield from(3.3引入的关键字)就是打开了一个双向通道,把外层调用方和最内层的子生成器连接起来
	 asyncio是python3.4引入的库
     async、await关键字是python3.5引入的关键字,也是现在最推荐的实现协程对象的方法。因为之前yield、yield from容易和生成器混淆,不能一眼看出这是在做协程。
多线程使用的是抢占式多任务处理(Pre-emptive Multitasking)  异步IO的机制为协作式多任务处理(Cooperative Multitasking)	

concurrent.futures

concurrent.futures中引入了future这个对象	
  Concurrent.futures中还有一个重要的对象叫做执行器(Executor),分为ThreadPoolExecutor和ProcessPoolExecutor两

asyncio

事件循环是asyncio的核心
     libuv is a multi-platform support library with a focus on asynchronous I/O.
asyncio 使用单线程、单个进程的方式切换
 async/await 是 Python 提供的异步编程 API,
 而 asyncio 只是一个利用 async/await API 进行异步编程的框架	

JavaScript--event loop(事件循环)

事件循环的并发模型,事件循环负责执行代码、收集和处理事件以及执行队列中的子任务
运行机制:
       1.所有同步任务都在主线程上执行,形成一个 执行栈(Execution Context Stack)
       2.主线程之外,还存在一个 任务队列(Task Queue)。
	       只要异步任务有了运行结果,就在 任务队列 之中放置一个事件
       3.一旦 执行栈 中的所有同步任务执行完毕,系统就会读取 任务队列,
	        看看里面有哪些待执行事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行
       4.主线程不断重复上面的第三步

浏览器中 JavaScript 的执行流程和 Node.js 中的流程都是基于 事件循环 的
在JavaScript中,所有的任务都可以分为
    同步任务:立即执行的任务,同步任务一般会直接进入到主线程中执行
    异步任务:异步执行的任务,比如ajax网络请求,setTimeout定时函数等
调用一个方法的时候,生成一个与这个方法对应的执行环境(context),又叫执行上下文
 事件队列(Task Queue
   主线程处于闲置状态时,主线程会去查找事件队列是否有任务。
      如果有,那么主线程会从中取出排在第一位的事件,并把这个事件对应的回调放入执行栈中,然后执行其中的同步代码  
 异步任务之间并不相同,因此他们的执行优先级也有区别。不同的异步任务被分为两类:
  微任务(micro task)和宏任务(macro task)
    宏任务与微任务的区别在于队列中事件的执行优先级
 node中事件循环的实现是依靠的libuv引擎。其他则是浏览器运行环境中由浏览器内核引擎实现	

迭代器与生成器

 迭代器就是可以被迭代的对象,对其使用next操作可以返回一个元素,通常多次迭代后迭代器会中止,此时迭代器无法再使用。
    比如python中可以通过iter方法来将一个列表转换成迭代器:
 python中,可以使用yield关键字使函数返回生成器对象	
   Python 3.5 添加了 async 和 await 这两个关键字,分别用来替换 asyncio.coroutine 和 yield from。
   自此,协程成为新的语法,而不再是一种生成器类型了

asyncio的运行原理

 了解EventLoop的设计原理	
    事件循环的创建、获取、设置
    运行和停止事件循环
    创建Future和Task
	事件循环的时钟
	计划执行回调函数(CallBacks
 
 阻塞类型
     IO调用,如socket,file,pipe等。
     人为制造的阻塞,如sleep。
     异步调用。
 层次:
  01.应用开发者通常应当使用高层级的 asyncio 函数,例如 asyncio.run(),
       首先创建了一个全新的事件循环。
	    一旦成功创建,就会接受我们传递给它的任何协程,并运行它直到完成,然后返回结果。
	   此函数还将对主协程完成后可能继续运行的内容进行清理,
	    一切完成后,它会关闭并结束事件循环。
  02. 低层级代码、库和框架的编写者,他们需要更细致地控制事件循环行为  
    使用asyncio来进行app开发  框架开发者 
      asyncio.get_running_loop()
      asyncio.get_event_loop()
      asyncio.set_event_loop(loop)
      asyncio.new_event_loop()	

      loop.run_until_complete(future)
      loop.run_forever() 
	   
	   loop.create_future()
       loop.create_task(coro, *, name=None, context=None)
       loop.set_task_factory(factory)
    coroutine loop.create_connection
    coroutine loop.create_datagram_endpoint
 传统的asyncio异步事件循环 
   01.如果不存在事件循环,必须创建一个事件循环--定义协程
   02.调用异步函数前要先调用asyncio.get_event_loop()函数获取事件循环loop对象,
   03.然后通过不同的策略调用loop.run_forever()方法或者loop.run_until_complete()方法执行异步函数	
       loop = asyncio.get_event_loop()  1
       task = loop.create_task(main())  2
       loop.run_until_complete(task)  3
       pending = asyncio.all_tasks(loop=loop)
       for task in pending:
           task.cancel()
       group = asyncio.gather(*pending, return_exceptions=True)  4
       loop.run_until_complete(group)  3
       loop.close()  5			

await一个协程。一般用于在一个协程中调用另一协程 要暂停执行,可使用 await 关键字
用asyncio.create_task()方法将Coroutine(协程)封装为Task(任务)。一般用于实现异步并发操作
      任务是协程的包装器,它安排协程尽快在事件循环上运行,并提供一系列的方法来获取协程的运行状态和返回值	

应用开发

syncio.run是Python3.7之后新增的入口函数-是asyncio.run()方法,可以省去显式的定义事件循环的步骤。
启动:启动异步应用程序的标准方式是有一个main()协程函数,并用asyncio.run()调用它
    协程函数,定义形式为 async def 的函数。
 |># more main.py
    import asyncio
    async def hello():
        print('enter hello ...')
        return 'world'
    
    if __name__ == "__main__":
        rst = asyncio.run(hello())
        print(rst)

开发

使用 asyncio 上手还是比较简单。主要是理解事件循环,协程和任务,future的关系			

java中

 进程(Process):将程序运行起来,我们称之为进程。进程是执行程序的一次执行过程,它是动态的概念
 
 线程(Thread):线程是进程中的实际运作的单位,是进程的一条流水线,是程序的实际执行者,是最小的执行单位。
   通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程。
   线程是CPU调度和执行的最小单位
 线程分为用户线程和守护线程。
     守护(Deamon)线程
    java.util.concurrent.locks.Lock
 线程实现:主线程和自定义线程 
    继承Thread类 扩展java.lang.Thread类
	实现Runnable接口 实现java.lang.Runnable接口

C++

1.Linux下一个进程在内存里有三部分的数据,就是“代码段”、“堆栈段”和“数据段”。


2. C++ 11之前: 操作系统平台提供的API,比如Linux的<pthread.h>,或者windows下的<windows.h> 。
 C++11提供了语言层面上的多线程,包含在头文件<thread>中		
  多线程并发指的是在同一个进程中执行多个线程   
    std::thread   
        std::thread是C++ 11提供的原生线程库,它简化了多线程编程,提供了线程创建、管理和同步等基本功能 
	Boost.Thread库
        Boost.Thread库是Boost C++库中的一个子库,提供了线程创建、管理和同步等功能。相较于std::thread,Boost.Thread库提供了更丰富的功能
		
  C++提供如下的异步操作接口:
     std::future :异步指向某个任务,然后通过future特性去获取任务函数的返回结果。
     std::aysnc :异步运行某个任务函数。 std::promise :线程1设置了某个值,然后通知另外的线程,可以省去消息通信。
     std::packaged_task :将任务和feature绑定在一起的模板,是一种对任务的封装
         C++标准库使用std::future为一次性事件建模
		
   C++带有线程操作,异步操作,就是没有线程池。
     https://github.com/progschj/ThreadPool/blob/master/ThreadPool.h
        原理是管理一个任务队列和一个工作线程队列,
		  工作线程 不断的从任务队列取任务,然后执行;
		  如果没有任务就挂起休眠等待新任务的到来。
		添加新任务的时候先添加到 任务队列,然后通知任意一个线程notify_one
	    // 工作线程组 need to keep track of threads so we can join them
        std::vector< std::thread > workers;
        // 任务队列 the task queue
        std::queue< std::function<void()> > tasks;
		
    维护一个任务队列用于管理待执行任务。
      可使用线程安全的容器(例如deque),配合互斥量(std::mutex)和条件变量(std::condition_variable)实现任务队列的同步访问。


     C++中原子变量(atomic)是一种多线程编程中常用的同步机制,它能够确保对共享变量的操作在执行时不会被其他线程的操作干扰,
	    从而避免竞态条件(race condition)和死锁(deadlock)等问题.
	
     unistd.h为Linux/Unix系统中内置头文件,包含了许多系统服务的函数原型,例如read函数、write函数、getopt函数和getpid函数等		

linux

 进程 
   内核为每个进程分配一个 PCB(Processing Control Block)进程控制块,维护进程相关的信息
     ps 查看当前终端的进程 getpid库函数的功能是获取本程序运行时进程的编号
     fork() 因为一个进程在运行中,如果使用了fork函数,就产生了另一个进程
	 kill 
     其一如何将新建连接分发给子进程,其二如何将数据/信号传给子进程,并监控子进程
 线程 
  Linux系统下的多线程遵循POSIX线程接口,称为pthread。
  编写Linux下的多线程程序,需要使用头文件pthread.h,连接时需要使用库libpthread.a	 

多进程代表的web server是Nginx,Apache等,多线程的有Varnish,gRPC,libevent库
 aioprocessing

参考

  https://docs.python.org/zh-cn/3/library/asyncio-task.html			
  https://zhuanlan.zhihu.com/p/343232126
  https://github.com/progschj/ThreadPool/blob/master/example.cpp
  https://blog.yanjingang.com/?p=4487
  https://zhuanlan.zhihu.com/p/636156144