【操作系统知识】多线程、多进程、并发并行、锁的梳理

本文详细梳理了操作系统中的并发与并行概念,包括线程与进程的差异、并发与并行的应用场景。讨论了同步与异步、阻塞与非阻塞,以及孤儿进程、僵尸进程和守护进程的相关知识。同时,介绍了进程池、守护线程和线程池的工作原理与使用,并探讨了线程安全、资源竞争以及锁的类型,如互斥锁和可重入锁,最后提到了死锁问题。通过本文,读者能深入理解操作系统的多线程和进程管理机制。
摘要由CSDN通过智能技术生成

1. 并发与并行

(1) 并发

并发是指同一时间间隔内多个任务都在运行,但是并不会在用一时刻同时运行,存在交替执行的情况,因此也经常存在资源竞争德尔情况。线程是并发的,实现的库有threading。

(2) 并行

并行指的是同一时刻多个任务同时运行,进程是并行的,实现的库有multiprocessing。

(3) 并发与并行的应用场景

IO密集型操作(程序需要执行较多的读写,请求和回复任务的需要CPU对硬盘、内存进行读写,如爬虫)应使用并发更好,类似于多线程。

CPU密集型操作(程序运行需要花大量时间做逻辑判断、运算等消耗CUP资源的操作,如复杂的浮点运算)应使用并行更好,类似于多进程。

2. 同步与异步

同步和异步都是相对于多任务而言的。

同步:多个任务之间有先后顺序执行,一个执行完才能执行下一个。

异步:多个任务之间没有先后顺序,可以同时执行,有时候一个任务可能在必要的时候获取另一个同时执行的任务的结果,这叫回调。

3. 阻塞与非阻塞

阻塞非阻塞是相对于代码执行而言的。

阻塞:如果卡住了调用者,调用者不能继续往下执行,当前线程会被挂起,调用线程只有在得到结果之后才会返回,就是说调用者阻塞了,函数只有在得到结果之后才会将阻塞的线程激活。

如果不会卡住,可以继续执行就是非阻塞的,非阻塞调用指在不能立刻得到结果之前也会立刻返回,同时该函数不会阻塞当前线程。

10. 孤儿进程、僵尸进程、守护进程

孤儿进程:父进程退出,子进程还在运行这些子进程都是孤儿进程,孤儿进程将被init进程(进程号为1)说收养,并由init进程对他们完成状态收集工作。

僵尸进程:进程使用fork创建子进程,如果进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的描述符仍然保存在系统中,这些进程就是僵尸进程。避免僵尸进程的方法:

  • fork两次用孙子进程去完成子进程的任务;
  • 用wait()函数使父进程阻塞;
    -使用信号量,在signal handler中调用waitpid,这样父进程不用阻塞。

守护进程:就是会随着主进程结束而结束的进程,主进程代码执行结束后就终止,而且守护进程内无法开启子进程,否则抛出异常。

import os
import time
from multiprocessing import Process

class MyProcess(Process):
    def __init__(self, name):
        super().__init__()
        self.name = name

    def run(self):
        print(f"进程为{os.getpid()},父进程为{os.getppid()}")
        print(f"我的名字是{self.name}")

if __name__ == "__main__":
    p1 = MyProcess("张三")
    p2 = MyProcess("李四")
    p2.daemon = True  # 默认为False,必须在start()之前上设置
    p1.start()
    p2.start()
    time.sleep(2)
    print("主进程结束")

# 结果
'''
进程为436,父进程为11972
我的名字是张三
进程为12140,父进程为11972
我的名字是李四
主进程结束
'''

11. 进程池

Pool进程池模块

Multiprocessing.Pool可以提供指定数量的进程供用户调用,当有新的请求提交到pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到规定最大值,那么该请求就会等待,直到池中有进程结束,才会创建新的进程来执行它。Pool类用于需要执行的目标很多,而手动限制进程数量又太繁琐时,如果目标少且不用控制进程数量则可以用Process类。

进程池Pool语法:Pool([processes[, initializer[, initargs[, maxtasksperchild[, context]]]]])

  • processes: 进程数,由CPU个数决定,不能操作CPU总数;如果进程是None,那么返回的数字os.cpu_count()
  • initializer: 如果是None,那么每一个工作进程在开始会调用initialize(*initargs)
  • maxtasksperchild: 工作进程退出前可以完成的任务数,完成后用一个新的工作进程来代替原进程,让闲置下来的资源被释放。默认值为None,表示只要Pool存在工作进程就回一直存活。
  • context: 用在只当工作进程启动时的上下文,一般使用multiprocessing.Pool()或者一个context()对象的Pool()方法来创建一个池。

multiprocessing 模块下的Pool类下的方法:

方法 描述
apply(func[, args=()[, kwargs={}]]) 该函数用于传递不定参数,和python中的apply函数一致,主进程会被阻塞知道函数执行结束(不建议使用,python3已经移除)。
apply_async(func[, args=()[, kwargs={}[, callback[, error_callback]]]]) 与apply的用法一致,但是它是非阻塞的且支持结果返回后进行回调,调用失败,将调用error_callback
map(func, iterable[, chunksize=None]) Pool类中的map方法,与内置的map函数用法基本一致,它会使进程阻塞知道结果返回。(注意:虽然第二个参数是一个迭代器,但在实际使用中,必须在整个队列都就绪后,程序才会运行子进程。)
map_async(func, iterable[, chunksize[, callback[, error_callback]]]) 与map用法一致,但是它是非阻塞的。
imap(func, iterable[, chunksize])
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值