并发编程

发布时间 2023-07-05 19:11:53作者: 别管鱼油我了

进程、线程

进程、线程是操作系统中的概念

进程是操作系统的一个独立单位,进程、线程都是操作系统中的基本概念,也就是说进程和线程都是操作系统层面的东西,进程和线程都是操作系统来调度的,不是程序员自己操控的。(与后续所学的协程做对比,协程不是操作系统的概念,是程序员层面的,由程序员自己调用,不是操作系统调用的,操作系统中没有这个概念)

进程

进程的用处:用来做任务的,听歌、看视频等。

进程简单来说就是一个过程,而线程是真正干活的。

进程和线程的区别

1、程序是死的东西,无任何生命意义。

2、进程是有生命周期的,当一个程序结束,进程就不存在了。

进程中要有线程,进程中如果没有线程是无意义的,一个进程中可以有多个线程,一个进程中至少有一个线程。

CPU工作机制(来回切换做到的)

当cpu遇到I/O系统的时候,会剥夺CPU的执行权限

当遇到的任务需要占用大量时间时,也会剥夺执行权限

I/O密集型:

遇到阻塞,不会占用大量CPU资源,需要等待

计算密集型:

没有遇到阻塞,但是需要占用大量CPU资源,也不需要等待

 

操作系统的调度算法

1、先来先服务调度算法

2、短作业优先调度算法

3、时间片轮转法(按时间)

4、多级反馈队列(以上三种不适用就用这种)

操作系统会自行选择哪一种算法。

 

进程的并发和并发

并行:同一时刻,同时执行。

想要同一时刻同时执行,要cpu有多个处理器(核)

单核的cpu无法并行

并发:在一段时间内,看起来是同时执行任务,不是同一时刻。

 

同步异步阻塞非阻塞

同步异步

关注的是消息通信机制。

同步:调用者主动等待调用结果

异步:调用发出后,不会立即得到结果,而是调用发出发出后,被调用者通过状态通知来告诉调用者。

举个例子:

例如买书,

如果是同步通讯机制:问书店老板是否有这本书,老板说查一下,稍等会,然后购买者就等,等到老板返回结果。

如果是异步通讯机制:问老板是否有这本书,老板说找到后给购买者打电话。但是其他人在此时也可以询问老板是否有书。

更多关注老板

阻塞非阻塞

关注的是程序在等待调用结果时的状态。

堵塞:调用结果返回之前,线程会被挂起,直到得到结果,才会返回。

非堵塞:不立刻得到结果之前,该调用不会阻塞当前线程。

例如:阻塞时调用:在书店老板找到这本书之前,会一直挂起,直到找到这本书。

非阻塞调用:在问书店老板是否有在这本书,不管有没有,都会去一边,偶尔返回看老板是否有给结果。(这个期间别人用不了这个窗口)

更多关注自己

 

同步阻塞,同步非阻塞,异步阻塞,异步非阻塞

 

开启进程

使用的模块:multiprocess

在windows系统,开启进程必须写在__main__中,其他系统不用加。

from multiprocessing import Process

def task():
    with open('a.txt','w',encoding='utf8') as f:
        f.write('helloworld')

if __name__ == '__main__':
    p = Process(target=task)     #还没开始,只是实例化一个对象
    p.start()                      #调用这个方法开始进程

    

 

Process类的参数

group=None, target=None, name=None, args=(), kwargs={},*, daemon=None

每一个进程都有:进程名,进程id等属性
name: 代表的是进程的名字
args=(), 传参数的
kwargs={},
daemon:守护进程

关于子进程和主进程:他们并不是包含关系,只是主进程带动了子进程而已,而在下列例子中task是子进程,调用task是主进程,而py文件又是前一个的主进程,当py文件是子进程的时候,pycharm是主进程。

简单的举例 :

from multiprocessing import Process

def task(name,age):
    print('hi')
    print('name:',name)
    print('age:',age)

if __name__ == '__main__':

    # group=None, target=None, name=None, args=(), kwargs={},*, daemon=None
    p = Process(target=task,name='lin',args=('rui',),kwargs={'age':18})
    p.start()
    #查看进程名字
    print(p.name)

'''
lin   #先走主进程,因为子进程开启需要时间,所以在主进程走完才会走子进程
hi
name: rui
age: 18

'''

加入daemon(守护进程)

from multiprocessing import Process

def task(name,age):
    print('hi')
    print('name:',name)
    print('age:',age)

if __name__ == '__main__':

    # group=None, target=None, name=None, args=(), kwargs={},*, daemon=None
    p = Process(target=task,name='lin',args=('rui',),kwargs={'age':18})
    p.daemon = True  ##守护进程的作用:主进程结束,子进程也结束,并且守护进程放在start之前,默认是False
    p.start()
    #查看进程名字
    print(p.name)

 

方法介绍

join  等待子进程结束,再执行主进程

from multiprocessing import Process

def task(name,age):
    print('hi')
    print('name:',name)
    print('age:',age)

if __name__ == '__main__':

    # group=None, target=None, name=None, args=(), kwargs={},*, daemon=None
    p = Process(target=task,name='lin',args=('rui',),kwargs={'age':18})
    # p.daemon = True    #守护进程的作用:主进程结束,子进程也结束,并且守护进程放在start之前
    p.start()
    p.join()            #先执行子进程再执行主进程
    #查看进程名字
    print(p.name)
    
'''
结果:   因为加了join所以先执行子进程,子进程走完才走主进程
hi
name: rui
age: 18
lin
'''

 

terminate  通知操作系统杀死进程(相当于任务管理器的结束任务)
is_alive()  判断进程是否存在,布尔值
from multiprocessing import Process

def task(name,age):
    print('hi')
    print('name:',name)
    print('age:',age)

if __name__ == '__main__':

    # group=None, target=None, name=None, args=(), kwargs={},*, daemon=None
    p = Process(target=task,name='lin',args=('rui',),kwargs={'age':18})
    p.start()
    p.terminate()     #杀死进程
    import time
    #查看进程名字
    time.sleep(1)
    print(p.is_alive())    #判断进程是否存在
    print(p.name)    #杀死的只是task这个子进程,主进程还存活,可以运行

'''
结果:  
False   
lin
'''

join 执行完子进程,子进程完毕再执行主进程

(例子在多进程循环中展示)

如何开启多进程

因为进程需要时间,所以进程开启需要时间,所以执行无顺序。

关于join:

当join放进循环,会串行执行,时间长。

# 加入循环,多次传值
from multiprocessing import Process
import time

def task(i):
    print('hi:',i)
    time.sleep(1)


if __name__ == '__main__':
    start = time.time()
    for i in range(5):
        # group=None, target=None, name=None, args=(), kwargs={},*, daemon=None
        p = Process(target=task,args=(i,))
        p.start()
        p.join()
    print('hihihi')
    print('time:',time.time()-start)
'''
结果:
hi: 0
hi: 1
hi: 2
hi: 3
hi: 4
hihihi
time: 5.777955770492554    虽然有了顺序,但是时间会变得很慢,进程就无意义 
'''

当join存放在子进程分别执行完,就会并行执行,时间短。

# 加入循环,多次传值
from multiprocessing import Process
import time

def task(i):
    print('hi:',i)
    time.sleep(1)

if __name__ == '__main__':
    l = []
    start_time = time.time()
    for i in range(5):
        p = Process(target=task,args=(i,))
        p.start()
        l.append(p)
    for j in l:
        j.join()
    print('123')
    print(time.time() - start_time)

'''
结果:
hi: 0
hi: 1
hi: 2
hi: 3
hi: 4
123
1.186903953552246
'''

进程锁

锁:为了保证数据安全。

lock.acquire() 上锁

lock.release()释放锁

为了防止调用导致的调用进程时间短,程序要按顺序并且绑在一起执行。

from multiprocessing import Process
from multiprocessing import Lock
def task(i,lock):
    lock.acquire()      #上锁
    print('%s进程来了'%i)
    print('%s进程走了'%i)
    lock.release()     #释放锁

if __name__ == '__main__':
    lock = Lock()
    for i in range(5):
        p = Process(target=task,args=(i,lock))
        p.start()

'''
结果:
1进程来了
1进程走了
0进程来了
0进程走了
2进程来了
2进程走了
3进程来了
3进程走了
4进程来了
4进程走了
'''

进程间的通信(IPC机制)

进程是可以开启多个的,进程和进程之间相互独立,互补影响,进程是操作系统一个独立单元。

一个进程崩溃了,其他进程不受影响。

一个进程中不能修改另一个进程的数据,言外之意是进程之间的数据是隔离的,互不影响

想要在一个进程中使用一个进程的数据,需要让进程之间通信。