Python之并发编程(进程)

news2025/3/13 15:20:07

文章目录

  • 一、操作系统的发展史
  • 二、进程基础(操作系统中的概念)
    • 1.什么是进程
    • 2.进程的调度算法
    • 3.进程的并行与并发
    • 4.进程的三状态
    • 5.同步异步
    • 6.阻塞与非阻塞
    • 7.同步异步与阻塞非阻塞综合使用
  • 三、如何创建进程
    • Process的几个方法
    • 如何开启多进程
    • 进程间的数据默认隔离
    • 基于TCP协议的高并发程序

一、操作系统的发展史

	首先回顾三大核心硬件
		CPU 是计算机中那个真真一直在运行干活的人
		内存 给CPU准备需要运行的代码 软件
		硬盘 用来存储将可能要被运行的代码 软件

进入主题

	1.穿孔卡片
		1725年,法国人布乔发明了打孔卡,用于贮存纺织机工作过程控制的信息。
		但当时打孔卡并没有广泛应用。19世纪80年代,打孔卡主要用于记录数据,
		曾用于记录美国1890年人口普查数据。
		(CPU利用率非常的低 好处就是程序员可以一个人来独占计算机 想做什么就做什么)

在这里插入图片描述

	2.联机批处理系统
		加载在计算机上的一个系统软件,在它的控制下,计算机能够自动地、成批地处理
		一个或多个用户的作业(这作业包括程序、数据和命令)。
		缩短录入数据的时候 让CPU连续工作的时间变长(提升CPU利用率)

在这里插入图片描述

	3.脱机批处理系统
		为克服与缓解:高速主机与慢速外设的矛盾,提高CPU的利用率
		又引入了脱机批处理系统,即输入/输出脱离主机控制。
		(现代计算机的雏形主要提升CPU利用率)

在这里插入图片描述

	单道技术
		所有的程序排队等着被运行 总耗时是所有程序耗时之和
		(比如打游戏听歌>必须的听完歌等着播完>之后才能去打游戏)
		
	多道技术
		计算机利用空闲时间提前准备好一些数据 提高效率 总耗时较短
		(比如听歌>这一首歌还没唱完>就已经加载好了下一首歌的歌词歌曲)
	
	多道技术简单的来说就是 切换+保存状态
		1.CPU在两种下会切换(去执行其他的程序)
			1> 程序自身进入IO操作(IO:输入输出操作)
				例如:获取用户输入、time.sleep、读取文件、保存文件
		2.保存状态
			每次切换之前要记录下当前执行的状态 之后切换回来基于当前状态继续执行
			 	例如:打游戏撩妹 文字已经打入聊天框 游戏开始了 打完文字还在聊天框内

	eg:
	  一个人要同时使用五台打印机打印资料,请问如何要一个人连续使用打印机打印资料?
		启动第一台打印机打印资料之后,第一台在打印过程中就可以去启动第二台打印机执行打印任务,依次类推...

二、进程基础(操作系统中的概念)

进程(资源单位):一个正在运行的程序,操作系统中正在运行的应用程序。进程之间是相互物理隔离的。因为如果未进行隔离的话,如果这一个程序被植入病毒入侵,那么就可以通过这个进程进而其它的进程,所以进程之间是互相隔离的。进程之间无法直接通信,需要通过特定的技术

线程(执行单位):操作系统能够机械能运行调度的最小单位。它被包含在进程中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程执行不同的任务

进程内必须有一条或之上的线程,每条线程执行不同的任务

1.什么是进程

程序:一堆躺在文件夹里面的代码(还没有运行)

其实是一个死的东西,一堆代码就是程序,它没有生命周期

进程:正在被运行的程序(代码正在运行)

它是有生命周期,这个任务做完,进程就不存在了

举个例子形象的说明:

  • 计算机的核心是CPU,它承担了所有的计算任务,它就像一座工厂,时刻在运行
  • 假定工厂的电力有限,一次只能供给一个车间使用。也就是说,一个车间开工的时候,其他车间都必须停工。背后的含义就是,单个CPU一次只能运行一个任务。
  • 进程就好比工厂的车间,它代表CPU所能处理的单个任务。任一时刻,CPU总是运行一个进程,其他进程处于非运行状态。
  • 一个车间里,可以有很多工人。他们协同完成一个任务。
  • 线程就好比车间里的工人。一个进程可以包括多个线程。

或者用这个来说明:
厨师做饭,厨师做一道菜、应该有菜谱,按照菜谱的程序一步一步的做,在这个过程中,菜谱就是程序,程序就是路程. 做菜的过程就是进程,厨师就是线程,如果做菜完毕了,进程就不存在了,线程是进程中实际做事儿的。

2.进程的调度算法

进程和线程都是由操作系统来调度使用的,我们程序员是不能控制的,这里就涉及到了调度算法

'进程的调度算法'
	先来先服务算法:
		见名知意 第一个打开的程序先运行(针对后面打开耗时较短的程序不友好)

	短作业优先调度
		查看里面用时最少的程序先运行(针对耗时比较长的程序不友好)
	
	时间片轮转法+多级反馈队列
		将固定的时间均分成很多份,所有的程序来了都公平的分一份
		分配多次之后如果还有程序需要运行,则将其分到下一层(保证后面的程序能运行)
		越往下表示程序总耗时越长,每次分的时间片越多,就优先级越低

3.进程的并行与并发

	并行:统一时刻同时运行
		多个进程同时执行,必须要有多个CPU核心参与,单核CPU无法实现并行
		eg:
			如果CPU是单核的,同一时刻能不能够做到同时执行多个任务。答:不能
			如果是多核心的,同一时刻是能够做到的
			如果核心是2个,同一时刻最多执行两个任务
			如果核心是4个,同一时刻最多执行四个任务
		
	并发:一段时间内看起来是同时运行
		多个进程看上去像同时执行,单核CPU可以实现,多核心CPU肯定也可以实现
		eg:看视频、听音乐、浏览网页同时操作,这个单核是可以达到的,CPU的切换达到的

4.进程的三状态

在程序运行的过程中,由于被操作系统的调度算法控制,程序会进入 几个状态:就绪态、运行态和阻塞态

在这里插入图片描述

就绪态

  • 当进程已分配到除CPU以外的所有必要的资源,只要获得处理机便可立即执行,这时的进程状态称为就绪状态。即所有的进程在被CPU执行之前都必须先进入就绪态等待。

运行态

  • 执行/运行(Running)状态当状态已获得处理机,其进程正在处理机上执行,此时的进程状态称为执行状态。

阻塞态

  • 阻塞(Blocked)状态正在执行的进程,由于等待某个事件发生而无法执行时,便放弃处理机而处于阻塞状态。引起进程阻塞的事件可有多种,例如:等待I/O完成、申请缓冲区不能满足、等待信件(信号)等。即进程运行过程中出来了IO操作,阻塞态无法直接进行运行态,需要先进入就绪态。

创建态和终止态:进程开始和结束的状态

代码演示:
在这里插入图片描述

5.同步异步

	'同步异步主要用于描述任务的提交状态'
		同步:
			提交完成任务之后再原地等待任务的结果,期间不做任何事(单道技术)
			eg:去银行办理业务,选择排队等待(一直同步等待消息的通知)
		异步:
			提交完成任务之后,不在原地等待直接去做其他事情,结果自动提醒(多道技术)
			eg:取号排队,取号之后就可以做其他事,直到被窗口喊到取的号,再去柜台办理业务

6.阻塞与非阻塞

	'阻塞与非阻塞主要用于描述进程的状态'
		阻塞(阻塞态)	等待运行完再接着下一步
		非阻塞(就绪态、运行态)	无需等待直接下一步
	'''
	当一个任务因调用或IO要获取某个结果,阻塞调用必须等待调用的结果,
	非阻塞调用则可以不必等待这个结果而使进程始终处于就绪态或运行态
	'''

7.同步异步与阻塞非阻塞综合使用

	同步阻塞:在银行排队,并且在队伍中什么事情都不做
	同步非阻塞:在银行排队,并且在队伍中做点其他事情
	异步阻塞:取号,在旁边座位上等着叫号,期间不可做事
	异步非阻塞:取号,在旁边座位上等着叫号,期间可以做别的事情
	'异步非阻塞效率最高'
'''
很多人会把同步和阻塞混淆,是因为很多时候同步操作会以阻塞的形式表示出来,
同样的,很多人也会把异步和非阻塞混淆,因为异步操作一般都不会再真正的IO操作处被阻塞。
'''

三、如何创建进程

'''
创建进程的两种方法:
    1.鼠标双击软件图标(打开程序 底部代码运行就是创建进程)
    2.代码创建进程(两种方法)  需要用到模块mutiprocessing、Process
'''

说明:
在不同的操作系统中创建机场南底层原理不一样
	windows
	     以导入模块的形式创建进程,
	     需要用到if __name__ == '__main__':启动脚本(不然报错)
	      
	linux/mac
	     以拷贝代码的形式创建进程
	     不需要启动脚本 但是为了更好的兼容性 加上启动脚本更好


'''方法1:创建函数'''
from multiprocessing import Process   # 导入模块
import time

def task(name,age):
    print('task is running',name,age)
    time.sleep(1)
    print('task is over',name,age)


'''现在这个写法还没有开启进程'''
'''在windows平台上必须写在__main__里面'''
if __name__ == '__main__':
    'def __init__(self, group=None, target=None, name=None, args=(), kwargs={},'
    p1 = Process(target=task, kwargs={'name': 'tom', 'age': 19})  # 实例出来一个进程类,让这个进程执行task任务
    # p1 = Process(target=task,args=('jack',18))  # 位置传参
    p1.start()  # 真正的开启进程(异步)
    '''
    操作系统是负责把这个进程开起来
    开启一个进程来执行task任务,真正是谁执行的这个任务?是线程,进程里面至少有一个线程
    '''
    '''进程的几个属性:1.进程名,2.进程号pid kill'''
    # task('chen',20)  # (同步)
    print('主进程')

'''
运行结果为:
    同步方式下,先执行了task函数在进行主进程:
        task is running chen 20
        task is over chen 20
        主进程
    
    异步方式下:先执行了主进程,在进行了task函数
        主进程
        task is running tom 19
        task is over tom 19
'''


'''方法2:创建类'''
from multiprocessing import Process  # 导入模块
import time

class MyProcess(Process):   #  继承Process类
    def __init__(self,name):
        super().__init__()
        self.name = name

    def run(self):
        print('start run', self.name)
        time.sleep(1)  # 睡眠1秒后运行
        print('end run', self.name)


if __name__ == '__main__':
    obj = MyProcess('jack')
    obj.start()  # 启动子进程,只有对象才能去点子进程
    print('主进程')

'''底层原理都是,先运行完父类的代码,创建一个进程空间运行子类的代码'''

Process的几个方法

	'''Process的几个方法'''
from multiprocessing import Process
import time


def task(name, age, gender):
    print(name, age, gender)
    print('子进程')


if __name__ == '__main__':
    '''
    只是通知操作系统去开进程,并不是立马把进程开起来,它需要消耗一定的时间,侧面的反应了开启进程其实消耗很大
    '''
    p = Process(target=task, args=(), kwargs={'name': 'chen', 'age': 18, 'gender': 'male'})
    # 实例化出来一个进程类,让这个进程执行task任务
    p.start()  # 真正的开启进程(异步)
    # 操作系统是负责把这个进程开起来,开启一个进程来执行task任务
    # 真正谁执行这个任务,是线程,进程里面至少要有一个线程
    '''进程的几个属性:1.进程名,2.进程号pid'''
    # 如何查看进程的名称
    # print(p.name)  # Process-1

    # 如何修改进程名称
    p.name = 'hello'
    print(p.name)  # hello

    # 如何查看进程号
    print(p.pid)  # Process pid

    print(p.is_alive())  # True
    p.terminate()  # 杀死进程,结束任务
    # 系统并不是第一时间立马就杀死进程,需要给点时间
    time.sleep(1)
    print(p.is_alive())  # False

    p.join()  # 等待子进程的代码全部执行完毕,在走主进程的
    print('主进程')

如何开启多进程

	'''如何开启多进程'''
# 开启多进程就意味着可以同时做多个任务,一个进程做一个任务,多个进程肯定是做多个任务
from multiprocessing import Process
import time

def task(name):
    print(name)
    print('子进程')
    time.sleep(1)

if __name__ == '__main__':
    '''理论上你是可以一直开进程,但是你需要考虑资源的消耗情况'''
    start_time = time.time()
    ll = []
    for i in range(3):
        p = Process(target=task, kwargs = {'name':'jack'})  # 实例化一个进程类
        p.start()
        # p.join()  # 放在里面执行是同步串行 
        ll.append(p)

    for i in ll:
        i.join()

    print('主进程,总时间:',time.time() - start_time)
    

'''这种形式和下面的一样的意思,都是异步并发'''
if __name__ == '__main__':
    '''理论上你是可以一直开进程,但是你需要考虑资源的消耗情况'''
    start_time = time.time()

    p1 = Process(target=task, kwargs = {'name':'jack'})  # 实例化一个进程类
    p2 = Process(target=task, kwargs = {'name':'tom'})  # 实例化一个进程类
    p3 = Process(target=task, kwargs = {'name':'chen'})  # 实例化一个进程类
    '''
    这种就是异步并发,
    同一个时刻执行所有的任务
    结果耗时为1秒多
    '''
    p1.start()
    p2.start()
    p3.start()
    p2.join()
    p1.join()
    p3.join()
    '''
    这种就是同步串行,
    会等上一个执行完毕才执行下一个
    结果耗时为3秒多
    '''
    # p1.start()
    # p1.join()
    # p2.start()
    # p2.join()
    # p3.start()
    # p3.join()

    print('主进程,总时间:',time.time() - start_time)

进程间的数据默认隔离

'''
进程间数据默认隔离
    多个进程数据彼此之间默认是互相隔离的(每个有各自的进程空间,互不干扰)
    如果真的想要交互,需要借助 管道和队列
'''
from multiprocessing import Process
money = 100  # 父进程的全局数据

def task():
    global money
    money = 666  # 子进程的局部money数据
    print('子进程打印的money:',money)


if __name__ == '__main__':
    p = Process(target= task,)
    p.start()
    p.join()  # 等待子进程运行完毕后执行父进程
    print('父进程打印的money:',money)
'''
最后得到的结果为:
    子进程打印的money: 666
    父进程打印的money: 100
所以证明了是互相隔离的,互不干扰
'''

在这里插入图片描述

基于TCP协议的高并发程序

import socket
'''一个服务端不能够同时给多个客户端发送数据'''
from multiprocessing import Process

def task(conn):
    while True:
        try:
            # 异常一个bug:粘包现象
            data = conn.recv(2048) # 括号里面写的是接收的字节数,最多接收2048个

            if len(data) == 0:
                continue
            print(data)

            conn.send(data.upper())
        except Exception as f:
            print(f)
            break

    conn.close()


if __name__ == '__main__':
    '''
    1.买手机
    # SOCK_STREAM  ====> 代表的是TCP协议
    # socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # udp协议
    '''
    server = socket.socket()  # 默认是TCP协议

    '''
    2.买手机卡
     # '0.0.0.0'  =====> 代表允许任何的ip链接
    # server.bind(('0.0.0.0', 8000)) # 服务端绑定一个地址
    '''
    server.bind(('127.0.0.1', 12306))  # 服务端绑定一个地址

    '''
    3.开机
    '''
    server.listen(1)  # 监听 半连接池
    print('服务端正在准备接收客户端消息:')
    while True:
        conn,addr = server.accept()
        # 接收,程序启动之后,会在accept这里夯住,阻塞
        p = Process(target = task, args=(conn,))
        p.start()


'''可以同时运行多个客户端并且实现并发'''
'''客户端'''
import socket

client = socket.socket()

# 连接服务端的IP地址和端口号
client.connect(('127.0.0.1',12306))
while True:
    # 发送数据给服务端
    mag = input('请输入发送的数据>>>>:').strip()
    client.send(mag.encode('utf-8'))

    # 接收服务端回应的数据 最大字节数
    data = client.recv(2048)
    print(f"接收都服务端的数据:",data.decode('utf-8'))

# 断开连接
client.close()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1104036.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Qt控件之QButtonGroup】概述及使用

概述 QButtonGroup 类提供了一个容器来组织一组按钮部件。 QButtonGroup 提供了一个抽象容器,可以将按钮部件放置其中。它不提供此容器的可视表示(请参见 QGroupBox,用于容器部件),而是管理组中每个按钮的状态。 一个…

Electron webview 内网页 与 preload、 渲染进程、主进程的常规通信 以及企业级开发终极简化通信方式汇总

Electron 嵌入的页面中注入的是 preload.js 通过在标签中给 prelaod赋值,这里提到了 file://前缀,以及静态目录 static 怎么获取 实际代码,其中__static就是我们存放静态文件的地方,这个 static 是 electron 源代码根目录下的文件…

使用unordered_write调优RocksDB写性能

在使用rocksdb存储的服务中,我们发现QPS在4w/s就怎么调整都上不去了,写性能受到了某种限制。为什么呢?下图描述了rocksdb写入的流程。我们发现 unordered_write true可以提高写入吞吐量。 rocksdb的数据正常写入流程是,多个线程形…

九月 NFT 行业解读:熊市情绪仍占上风

作者: stellafootprint.network 9 月,著名主流媒体《滚石》(Rolling Stone)发表了一篇题为《你的 NFT 实际上——终于——完全不值钱了》(Your NFTs Are Actually — Finally — Totally Worthless)的文章&#xff0c…

【网络编程】从网络编程、TCP/IP开始到BIO、NIO入门知识(未完待续...)

目录 前言前置知识一、计算机网络体系结构二、TCP/IP协议族2.1 简介*2.2 TCP/IP网络传输中的数据2.3 地址和端口号2.4 小总结 三、TCP/UDP特性3.1 TCP特性TCP 3次握手TCP 4次挥手TCP头部结构体 3.2 UDP特性 四、总结 课程内容一、网络通信编程基础知识1.1 什么是Socket1.2 长连…

NumPy基础及取值操作

目录 第1关:ndarray对象 相关知识 怎样安装NumPy 什么是ndarray对象 如何实例化ndarray对象 使用array函数实例化ndarray对象 使用zeros,ones,empty函数实例化ndarray对象 代码文件 第2关:形状操作 相关知识 怎样改变n…

液压自动化成套设备比例阀放大器

液压电气成套设备的比例阀放大器是一种电子控制设备,用于控制液压动力系统中的液压比例阀1。 比例阀放大器通常采用电子信号进行控制,以控制比例阀的开度和流量,以实现液压系统的可靠控制。比例阀放大器主要由以下组成部分: 驱动…

tomcat 服务器

tomcat 服务器 tomcat: 是一个开源的web应用服务器。区别nginx,nginx主要处理静态页面,那么动态请求(连接数据库,动态页面)并不是nginx的长处,动态的请求会交给tomcat进行处理。 nginx-----转发动态请求-…

Golang学习:基础知识篇(三)—— Map(集合)

Golang学习:基础知识篇(三)—— Map集合 前言什么是Golang?Map集合定义 Map综合实例补充 前言 很久之前就想学Go语言了,但是一直有其他东西要学,因为我学的是Java嘛,所以后面学的东西一直是跟J…

Element-UI 日期选择器--禁用未来日期

在做项目的时候经常会遇到一些报表需要填写日期,一般是填写当日及当日以前,这时候我们的日期选择器就需要进行一些限制,比如: 这样之后,就不会误填写到明天啦,下面让我们看一下代码实现 html页面代码 这里…

Unity ECS最新DOTS环境搭建教程

最近DOTS终于发布了正式的版本, 今天我们来基于Unity 2023.1.6来搭建DOTS 1.0.16的开发环境与注意事项。 对啦!这里有个游戏开发交流小组里面聚集了一帮热爱学习游戏的零基础小白,也有一些正在从事游戏开发的技术大佬,欢迎你来交流学习。 1…

Liunx C运算符

一、运算符 &#xff08;一&#xff09;算数运算符 1、float和double不能进行取余操作。 &#xff08;二&#xff09;关系运算符 1、逻辑非 2、逻辑与 例子&#xff1a; if(&#xff08;a-1&#xff09;<b&&b) 如果为真则自增&#xff0c;为假不自增 3、逻辑或 …

leaflet 绘制显示半圆形,扇形示例 (134)

第134个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+leaflet中绘制显示半圆形,扇形 。这里引用了一个插件,非常方便的绘制扇形和半圆形等。 直接复制下面的 vue+leaflet源代码,操作2分钟即可运行实现效果 文章目录 示例效果配置方式示例源代码(共77行)安装插件相…

虚拟内存技术的基本概念(局部性原理,特征,实现)

1.传统存储管理方式的特征、缺点 很多暂时用不到的数据也会长期占用内存&#xff0c;导致内存利用率不高。 1.一次性: 作业必须一次性全部装入内存后才能开始运行。 这会造成两个问题: ①作业很大时&#xff0c;不能全部装入内存&#xff0c;导致大作业无法运行; ②当大量作…

node多版本管理器nvm

node多版本管理器nvm 1、为何要使用node版本管理器2、nvm安装步骤2-1、卸载系统中的node2-2、下载nvm2-3、安装 3、维护node版本3-1、安装指定版本node3-2、查看本机已安装的所有node版本3-3、切换本机node版本 1、为何要使用node版本管理器 在日常开发中&#xff0c;难免会遇…

apache log4j漏洞复现

log4j是开源的java存储日志的框架&#xff0c;一般都是大企业用&#xff0c;小企业自带的日志功能足够使用&#xff0c;Log4j2是默认支持解析ldap/rmi协议的&#xff0c;打印的日志中包括ldap/rmi协议都行。 具体介绍参考以下文章&#xff1a; log4j2---基于vulhub的log4j2漏…

Triple协议的隐式参数传递过程

前言 Dubbo 框架的 RPC 调用除了可以传递正常的接口参数外&#xff0c;还支持隐式参数传递。 隐式参数的传递依赖 RpcContext 对象&#xff0c;它持有一个 Map 对象&#xff0c;消费者往 Map 里写入数据&#xff0c;客户端在发起 RPC 调用前会构建 RpcInvocation&#xff0c;然…

linux中如何配置静态ip模式?

对于Linux系统来说&#xff0c;静态IP模式是一种常见的网络配置方式。相比动态IP模式&#xff0c;静态IP模式可以提供更加稳定的网络连接和更好的网络性能。本文将介绍在Linux中如何配置静态IP模式。 一、准备工作 在开始配置之前&#xff0c;需要先做好以下准备工作&#xff1…

Python爬虫基础之Selenium详解

目录 1. Selenium简介2. 为什么使用Selenium&#xff1f;3. Selenium的安装4. Selenium的使用5. Selenium的元素定位6. Selenium的交互7. Chrome handless参考文献 原文地址&#xff1a;https://program-park.top/2023/10/16/reptile_3/ 本文章中所有内容仅供学习交流使用&…

k8s集群授权prometheus(集群外部署)

一、前言 在集群外部prometheus想要调用k8s集群的apiserver获取监控数据需要通过token和ca验证&#xff0c;在集群内部部署的prometheus就不会有这个情况&#xff0c;因为集群内部部署prometheus pod的时候就已经注入了访问集群的token和ca文件&#xff0c;所以以下就针对k8s集…