一、进程:
1、进程的概念:
在Python中,进程是计算机中运行的程序的一个独立实例。每个进程都有自己独立的内存空间、数据栈以及其他系统资源。Python提供了多种处理进程的方式,其中最常见的是使用内置的 multiprocessing 模块。
python实现进程号的获取
from os import getid
if __name__=="__main__":
print("当前程序的进程号是"+str(getpid()))
a=10
b=30
c=a=b
print(c)
print("现在程序的进程号是"+str(getpid()))
2、进程状态的理解:
主要可以分五个状态理解
1.创建状态
进程正在被创建,但尚未进入就绪状态。这个状态表示操作系统已经为进程分配了必要的资源,但进程还没有开始执行。
2. 就绪状态
来到这种状态说明进程已经被分配好除CPU(计算机的大脑,中央处理器,数据的处理中心)外的所有必要的准备条件资源,只要获得了CPU的可执行权就可以立即执行.
3.执行状态
即此时的进程已经获得了CPU的可执行权,进程的指令正在被处理器执行,在这个状态下进程正在执行其任务
4.阻塞状态
进程暂时无法执行通常是因为等待某个事件的发生(例如:在等待I/O操作完成、等待信号或等待某个资源),像时间模块的.sleep()使程序执行到这里时休眠了一段时间后继续执行,也使得进程进入了阻塞状态。
import time
if __name__=="__main__":
print("计算机正在启动")
time.sleeep(2)
print("计算机启动成功,你可以正常使用了")
5.终止/退出状态
进程已经执行完毕,或者由于某种原因被终止。在这个状态下,进程不再执行,并且可能会释放其占用的资源。
在整个状态转换的过程中,有两种状态是不会进行转换的 1、阻塞->运行:即使给阻塞状态进程分配了CPU去执行,也无法执行,调度不会从阻塞队列中挑选任务,而且CPU只有空闲时才能执行任务这个空闲只有调度来协调,调度永远从就绪的进程中选择去执行。就像顾客订餐已经出现退单了,一定是有一些不太愉快的地方,外卖小哥还继续送餐必然得不偿失,还不如考虑多接一些就绪状态的单。
2、就绪->阻塞:因为就绪状态根本就没有执行任何内容,自然无法进入阻塞状态。如果外卖小哥根本就没有接到单,就没有退单的可能性
3、进程的调度
进程调度是操作系统中的一个重要概念,指的是操作系统通过某种调度算法,按照一定的策略和优先级,决定哪个进程应该在CPU上执行的过程。进程调度的目的是有效地利用系统资源,提高系统性能,以及满足各个进程的执行需求。
4、创建进程案例
multiprocess模块里面的Process类起到了创建进程的作用。每一个Process类就代表了一个进程对象,这个对象可以理解为一个独立的进程,可以去做另外的任务
import time
import multiprocessing
def typing():
for i in range(3):
print("-------我在敲代码--------")
time.sleep(2)
def listening():
for i in range(3):
print("--------我在听音乐--------")
time.sleep(2)
if __name__=="__main__":
process1=multiprocessing.Process(target=typing) # target指明不同的进程任务执行不同函数体中的代码
process2=multiprocessing.Process(target=listening)
process1.start() # 启动process子进程
process2.start()
除了.start()启动子进程外还有 其他的方法
(1):is_alive():判断子线程是否还活着
(2):join([timeout]):等待多少秒或是等待子进程结束
(3):Terminate():不管任务是否完成,子进程立即终止
例:
def areacode(city):
print("电话区号正在思考和转化中......")
time.sleep(2)
if city=="北京":
print("010")
elif city=="上海":
print("021")
elif city=="广州":
print("020")
else:
print("不知道")
if __name__=="__main__":
city=input("请输入城市名称")
p1=multiprocessing.Process(target=areacode,args=(city,))
p1.start()
#p1.terminate() # 不管任务是否完成,子进程立即 终止
p1.join() # 等待子线程睡眠结束后并把结束的结果输出后才可以继续向下执行
print("欢迎使用")
5、队列与多进程
队列
进程之间的队列用multiprocessing模块里的类Queue。Queue中的put()方法实现向队列中存储数据,如果队列已满,此方法将阻塞至有空间可用为止,Queue中的get()方法返回队列中的一个项目,如果队列为空,此方法将阻塞,直到队列中有项目可用为止。
if __name__=="__main__":
myqueue=multiprocessing.Queue() # .Queue()可以在一个线程中把数据存到队列里,在另一个线程中把数据从队列里取出,排在队首的人先通过安检。
myqueue.put("第一个进入队列的") # .put() 实现向队列中存取数据,若队列已满,此方法将阻塞至有空间可用为止
myqueue.put("第二个进入队列的")
print(myqueue.get()) # .get() 返回队列中的一个项目,如果队列为空,此方法将阻塞直到队列中有项目可用为止
print(myqueue.get())
多进程案例
import multiprocessing
def my_function(arg1, arg2):
# 进程执行的任务
result = arg1 + arg2
print(f"Result: {result}")
if __name__ == "__main__":
# 创建多个进程对象
process1 = multiprocessing.Process(target=my_function, args=(10, 20))
process2 = multiprocessing.Process(target=my_function, args=(30, 40))
# 启动进程
process1.start()
process2.start()
# 等待进程结束
process1.join()
process2.join()
print("所有进程执行完成")
创建多进程的思路:
6、进程锁
首先,认识进程共享变量
在多进程编程中,进程共享变量是多个进程之间共享的数据。这意味着这些变量可以在不同的进程中被访问和修改,以实现进程之间的通信。然而,由于进程独立性和隔离性,直接的变量共享可能导致并发访问问题,因此通常需要使用进程同步机制来确保共享变量的正确访问。
在Python中,multiprocessing 模块提供了一些用于在进程之间共享数据的工具。以下是一些常见的进程共享变量的方法:
如果变量是整型可以使用如下语句去处理:num=multiprocessing.Value("d",10.0)其中Value是值的意思;参数"d"表示数值型,若用"c"则表示字符串型。一般传递的简单变量要么是数值型,要么是字符串型。num就变成一个值对象,在这个值对象中就是它的属性。如果输出10.0的值就用num.value
# 进程共享变量
import multiprocessing
import time
def test_x(x):
# global x
time.sleep(2)
x.value+=3
print(x.value)
def test2_x(x):
# global x
time.sleep(2)
x.value+=4
print(x.value)
if __name__=="__main__":
# x=5
# Value是值的意思,参数d表示数值型,num则变成一个值对象,在这个值对象中Value就是它的属性,如果想输出1.0的值就用num.value即可
num=multiprocessing.Value("d",1.0)
process1=multiprocessing.Process(target=test_x,args=(num,))
process2=multiprocessing.Process(target=test2_x,args=(num,))
process1.start()
process2.start()
#输出的结果是4,0和8.0
进程锁案例
实现百人抢百票的功能
import multiprocessing
import time
# 百进程抢百票
def buy_tickets(no,num,lock):
lock.acquire()
time.sleep(3)
if num.value>0:
print(no+"买到了票,座位号是"+str(int(num.value)))
if num.value>=1:
num.value-=1
lock.release()
if __name__=="__main__":
num=multiprocessing.Value("d",20)
lock=multiprocessing.Lock()
for i in range(1,21):
process1=multiprocessing.Process(target=buy_tickets,args=("客"+str(i),num,lock))
process1.start()
以上案例使用了 multiprocessing模块里面的Lock锁,可以保证一个进程完成后它所用的共享变量才能被其他进程所使用。在使用Lock锁时先创建一个Lock锁即 num=multiprocessing.Value("d",20),当共享变量用完之后代码也就执行完毕了,此时使用lock.release()释放锁,而释放的锁就可以被其他进程所使用了。
二、线程
1、线程概念:
线程是进程内的一个执行流程一个进程成可以有多个线程,每个线程都是独立运行的但他们共享相同的资源,如:内存空间文件描述等。线程是操作系统能够进行调度和执行的最小单元,它由线程标识符、程序计数器、寄存器集合和堆栈组成。
2、实现一个线程可有的方法:
线程是使用threading模块中的Thread类,即线程的类,这些方法与multiprocessingde的Process进程类的方法相同
- start():表示启动一个线程
- join():等待线程终止才能继续
- run():表示线程活动的方法
- getName():返回线程名称
- setName():设置线程名称
- isAlive():返回线程是否活动
案例:
import threading
import time
def english():
lists=["cat","hat","bee","face"]
for word in lists:
print(word)
time.sleep(1)
def chinese():
lists=["猫","帽子","蜜蜂","脸"]
for word in lists:
print(word)
time.sleep(1)
if __name__=="__main__":
thread1=threading.Thread(target=english)
thread2=threading.Thread(target=chinese)
thread1.start()
thread2.start()
以上案例使用threading模块里面的Thread类创建线程,输出的格式是英汉翻译的样式。除了此方法外还有另一种用线程类定义的写法
import threading
import time
class Transform(threading.Thread): # 继承threading.Thread
def __init__(self,lists):
threading.Thread.__init__(self) # 这里需要Therad初始化函数里的一些参数,则就需要把__init__继承过来
self.lists=lists
def run(self): #重写Thread的run()方法
for word in self.lists:
print(word)
time.sleep(2)
if __name__=="__main__":
thread1=Transform(["cat","hat","bee","face"])
thread2=Transform(["猫","帽子","蜜蜂","脸"])
thread1.start()
thread2.start()
总结:
- 自己编写的类继承
threading.Thread类 - 重写
__init__方法时,需要重用threading.Thread.__init__方法 - 重写
run()方法,在主线程中定义自己的类,启动该类就可以执行这段代码了。
3、线程锁与多线程
与进程锁和多进程的原理类似,
线程锁案例
import threading
import time
def buy_tickets(no,num,lock):
lock.acquire() # 获取锁
time.sleep(3)
if num.value>0:
print(no+"买到了票,座位号是"+str(int(num.value)))
if num.value>=1:
num.value-=1
lock.release() 释放锁
if __name__=="__main__":
num=100
lock=threading.Lock() 定义锁
for i in range(1,21):
process1=threading.Thread(target=buy_tickets,args=("客"+str(i),num,lock))
process1.start()
百线程案例
import threading
import time
def my_function(arg1, arg2):
# 进程执行的任务
result = arg1 + arg2
print(f"Result: {result}")
if __name__ == "__main__":
# 创建多个进程对象
process1 = threading.Thread(target=my_function, args=(10, 20))
process2 = threading.Thread(target=my_function, args=(30, 40))
# 启动进程
process1.start()
process2.start()
# 等待进程结束
process1.join()
process2.join()
print("所有进程执行完成")
三、线程与进程的异同之处
(以下是我访问Chatgpt的,可做参考)
同:
- 并发性: 无论是进程还是线程,它们都能够实现程序的并行执行,充分利用计算资源,提高程序的性能。
- 资源共享: 进程和线程都可以共享资源,但需要注意处理共享资源的同步问题,以避免竞争条件和数据不一致性。虽然进程之间的地址空间是独立的,但操作系统提供了一些机制,使得进程之间可以共享一些资源,这被称为进程间通信(IPC,Inter-Process Communication)例如:在文件里,一个进程写入文件另一个进程读取文件,还可以通过消息队列发送和接收消息实现进程间的通信。
- 提高性能: 在合适的场景下,无论是多进程还是多线程,都能够提高程序的性能,加速任务的执行速度。
异:
定义:
- 进程: 进程是程序执行的一个实例,有独立的地址空间和资源,是系统资源分配的基本单位。
- 线程: 线程是进程内的一个执行流程,共享相同的地址空间和资源,是进程内的基本执行单元。
资源独立性:
- 进程: 进程拥有独立的地址空间,相互之间不共享内存,资源相对独立。
- 线程: 线程共享进程的地址空间和资源,可以方便地进行数据共享。
创建和销毁的开销:
- 进程: 创建和销毁进程的开销相对较大,涉及到分配和释放独立的地址空间。
- 线程: 创建和销毁线程的开销相对较小,因为线程共享进程的资源。
通信方式:
- 进程: 进程之间的通信需要使用进程间通信(IPC)机制,如管道、消息队列、共享内存等。
- 线程: 线程之间可以通过共享内存直接通信,也可以使用线程同步机制,如锁、信号量等。
错误隔离:
- 进程: 进程之间是相互隔离的,一个进程的错误通常不会影响其他进程。
- 线程: 线程之间共享相同的地址空间,一个线程的错误可能影响整个进程的稳定性。
执行单元数量:
- 进程: 进程是独立的执行单元,各自拥有独立的执行流程。
- 线程: 线程是进程内的基本执行单元,多个线程共享相同的执行流程。
适用场景:
-
进程:
- 适用于需要独立性、错误隔离的场景。
- 适用于利用多核处理器进行计算密集型任务。
-
线程:
- 适用于需要轻量级、低开销、并且有大量 I/O 操作的任务。
- 适用于并发执行,提高程序响应性。
最后,创作不易,如果觉得文章能够帮助到你的话还请给点个赞,你的鼓励便是我创作的最大动力!感谢!