多线程与多进程(二)

简介: 多线程与多进程

线程锁

线程锁可以将临界区内的代码锁住,在同一时刻下,只有获得锁的线程可以进入,至于究竟哪个线程可以获得这把锁,则是由操作系统调度,或者是两个线程之间进行竞争,谁能先接触到锁,谁就能获得这把锁。

f20dc0537db0078837829b694a5e8824_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

使用锁,来保证在检查tasks中是否有元素时,tasks不能被修改。将上面的pop函数改写一下

lock = threading.Lock()
def pop():
    global tasks
    while tasks:
        time.sleep(2)
        # 获得锁
        lock.acquire()
        if tasks:
            print(tasks.pop())
        else:
            break
        # 释放锁
        lock.release()

通过使用线程锁将检查tasks和tasks.pop()两个动作锁起来,确保只有一个线程可以运行这两行代码,从而保证检查结果和检查后的结果一致。使用线程锁是为了避免资源竞争,但是如果锁使用的不合适,会让你的多线程程序退化为单线程程序。

如果我们将上面的pop函数改为下面这种,使用锁将整段代码全部锁住,那么这段代码和直接使用一个for循环相比速度差别不大,甚至会变得更慢(存在线程调度)。

def pop():
    global tasks
    lock.acquire()
    while tasks:
        time.sleep(2)
        if tasks:
            print(tasks.pop())
        else:
            break
    lock.release()

所以,线程锁使用一般在一些不是很耗时,但是会存在资源竞争的地方,程序中的一些耗时操作一般不放入临界区内。

87f1f2d6a1dc4cbba1ce94a7757dcbd7_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

进程

在python中多进程是没有GIL锁的,也就是说,多进程可以同时使用多个CPU核。说的形象一定就是,使用多线程,你的CPU使用率最多能到30%,而多进程可以达到90%以上。

ccb6e305266508fbcf51dc30c5a5d154_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

进程的创建

进程与线程的创建一致,只是不再是使用Thread而是使用Process,创建方法与线程一样,也可以通过三种方式进行创建,这里请读者自行实现。

import multiprocessing
# target即为进程内部要运行的函数
# args为函数所需要的参数,要以tuple类型传入
thread = multiprocessing.Process(target=compress, args=(picture_id, ))

进程的启动方式

与线程不同,进程的实现更依赖于操作系统,python中进程有三种启动方式

  • spawn(unix和windows系统都可以使用)
  • fork(只有unix系统可以使用)
  • forkserver(只有unix系统可以使用)

三种启动方式,其中fork的效率最高,spawn的效率最低。使用set_start_method方法可以设置特定的启动方法,注意,这个方法只能被调用一次,一般会放在if __name__ == '__main__':下面。这里分析spawnfork两种启动方式的差别。

spawn

spawn的启动方式是通过将一份代码使用python解释器运行多次,其中主进程的__name____main__,而通过主进程启动子进程的__name____mp_main__

from multiprocessing import (
    Process, 
    set_start_method
)
import os
print('Hello, world. __name__: %s' % __name__)
def func():
    print(os.getpid())
if __name__ == '__main__':
    set_start_method('spawn')
    p1 = Process(target=func)
    p2 = Process(target=func)
    p1.start()
    p2.start()
    p1.join()
    p2.join()

069f2542b5ce0e23c1adcd1089c5aa43_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

你会发现,hello,world被打印了三次,也印证了spawn方式是通过将同一份代码执行了三次。分别是主进程一次,和两个子进程各执行了一次。

fork

fork则是使用的系统调用,在调用fork的位置直接获得子进程和父进程

from multiprocessing import (
    Process, 
    set_start_method
)
import os
print('Hello, world. __name__: %s' % __name__)
def func():
    print(os.getpid())
if __name__ == '__main__':
    set_start_method('fork')
    p1 = Process(target=func)
    p2 = Process(target=func)
    p1.start()
    p2.start()
    p1.join()
    p2.join()

4721c6f7e7d14218985764c5ecf42e6e_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

fork方式只打印了一次hello,world,即在主进程启动的时候,在主进程创建子进程的时候,没有将整个代码重新运行一遍,而是只在创建时,直接获得了子进程。熟悉linux系统中fork的同学应该会比较熟悉这种方式。所以同样的一份代码,放到windows系统是可以运行,但是放到Linux系统或者是苹果的macos系统的上就不能运行了,原因就出在了进程的启动方式不同。在编写多进程代码时,这一点要格外注意。

相关文章
|
10天前
|
消息中间件 并行计算 安全
进程、线程、协程
【10月更文挑战第16天】进程、线程和协程是计算机程序执行的三种基本形式。进程是操作系统资源分配和调度的基本单位,具有独立的内存空间,稳定性高但资源消耗大。线程是进程内的执行单元,共享内存,轻量级且并发性好,但同步复杂。协程是用户态的轻量级调度单位,适用于高并发和IO密集型任务,资源消耗最小,但不支持多核并行。
29 1
|
22天前
|
存储 消息中间件 人工智能
进程,线程,协程 - 你了解多少?
本故事采用简洁明了的对话方式,尽洪荒之力让你在轻松无负担的氛围中,稍微深入地理解进程、线程和协程的相关原理知识
38 2
进程,线程,协程 - 你了解多少?
|
8天前
|
Python
Python中的多线程与多进程
本文将探讨Python中多线程和多进程的基本概念、使用场景以及实现方式。通过对比分析,我们将了解何时使用多线程或多进程更为合适,并提供一些实用的代码示例来帮助读者更好地理解这两种并发编程技术。
|
11天前
|
消息中间件 并行计算 安全
进程、线程、协程
【10月更文挑战第15天】进程、线程和协程是操作系统中三种不同的执行单元。进程是资源分配和调度的基本单位,每个进程有独立的内存空间;线程是进程内的执行路径,共享进程资源,切换成本较低;协程则更轻量,由用户态调度,适合处理高并发和IO密集型任务。进程提供高隔离性和安全性,线程支持高并发,协程则在资源消耗和调度灵活性方面表现优异。
35 2
|
2月前
|
存储 消息中间件 资源调度
「offer来了」进程线程有啥关系?10个知识点带你巩固操作系统基础知识
该文章总结了操作系统基础知识中的十个关键知识点,涵盖了进程与线程的概念及区别、进程间通信方式、线程同步机制、死锁现象及其预防方法、进程状态等内容,并通过具体实例帮助理解这些概念。
「offer来了」进程线程有啥关系?10个知识点带你巩固操作系统基础知识
|
17天前
|
算法 安全 调度
深入理解操作系统:进程与线程的管理
【10月更文挑战第9天】在数字世界的心脏跳动着的,不是别的,正是操作系统。它如同一位无形的指挥家,协调着硬件与软件的和谐合作。本文将揭开操作系统中进程与线程管理的神秘面纱,通过浅显易懂的语言和生动的比喻,带你走进这一复杂而又精妙的世界。我们将从进程的诞生讲起,探索线程的微妙关系,直至深入内核,理解调度算法的智慧。让我们一起跟随代码的脚步,解锁操作系统的更多秘密。
22 1
|
22天前
|
数据挖掘 程序员 调度
探索Python的并发编程:线程与进程的实战应用
【10月更文挑战第4天】 本文深入探讨了Python中实现并发编程的两种主要方式——线程和进程,通过对比分析它们的特点、适用场景以及在实际编程中的应用,为读者提供清晰的指导。同时,文章还介绍了一些高级并发模型如协程,并给出了性能优化的建议。
22 3
|
2天前
|
Linux 调度
探索操作系统核心:进程与线程管理
【10月更文挑战第24天】在数字世界的心脏,操作系统扮演着至关重要的角色。它不仅是计算机硬件与软件之间的桥梁,更是管理和调度资源的大管家。本文将深入探讨操作系统的两大基石——进程与线程,揭示它们如何协同工作以确保系统运行得井井有条。通过深入浅出的解释和直观的代码示例,我们将一起解锁操作系统的管理奥秘,理解其对计算任务高效执行的影响。
|
2月前
|
资源调度 算法 调度
深入浅出操作系统之进程与线程管理
【9月更文挑战第29天】在数字世界的庞大舞台上,操作系统扮演着不可或缺的角色,它如同一位精通多门艺术的导演,精心指挥着每一个进程和线程的演出。本文将通过浅显的语言,带你走进操作系统的内心世界,探索进程和线程的管理奥秘,让你对这位幕后英雄有更深的了解。
|
2月前
|
Java
直接拿来用:进程&进程池&线程&线程池
直接拿来用:进程&进程池&线程&线程池

相关实验场景

更多