iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >Python高级教程之线程进程和协程的代码解析
  • 449
分享到

Python高级教程之线程进程和协程的代码解析

2024-04-02 19:04:59 449人浏览 泡泡鱼

Python 官方文档:入门教程 => 点击学习

摘要

目录进程进程 5 种基本状态进程的特点进程间数据共享进程池进程的缺点线程线程的定义使用线程模块的简单示例代码解析协程协程与线程python 协程协程的执行关闭协程链接协程以创建管道总

进程

进程是指在系统中正在运行的一个应用程序,是 CPU 的最小工作单元。

进程 5 种基本状态

一个进程至少具有 5 种基本状态:初始态、就绪状态、等待(阻塞)状态、执行状态、终止状态。

  • 初始状态:进程刚被创建,由于其他进程正占有CPU资源,所以得不到执行,只能处于初始状态。
  • 就绪状态:只有处于就绪状态的经过调度才能到执行状态
  • 等待状态:进程等待某件事件完成
  • 执行状态:任意时刻处于执行状态的进程只能有一个(对于单核CPU来讲)。
  • 停止状态:进程结束

进程的特点

  • 动态性:进程是程序的一次执行过程,动态产生,动态消亡。
  • 独立性:进程是一个能独立运行的基本单元。是系统分配资源与调度的基本单元。
  • 并发性:任何进程都可以与其他进程并发执行。
  • 结构性:进程由程序、数据和进程控制块三部分组成。

multiprocessing 是比 fork 更高级的库,使用 multiprocessing 可以更加轻松的实现多进程程序。

#!/usr/bin/env Python
# -*- coding:utf-8 -*- 
from multiprocessing import Process
import threading
import time
def foo(i):
    print 'say hi',i
for i in range(10):
    p = Process(target=foo,args=(i,))
    p.start()

注意:由于进程之间的数据需要各自持有一份,所以创建进程需要的非常大的开销。并且python不能再windows下创建进程!

使用多进程的时候,最好是创建和和 CPU 核数相等的进程数。

进程间数据共享

系统中的进程与其他进程共享 CPU 和主存资源,为了更好的管理主存,操作系统提供了一种对主存的抽象概念,即为虚拟存储器(VM)。它也是一个抽象的概念,它为每一个进程提供了一个假象,即每个进程都在独占地使用主存。

虚拟存储器主要提供了三个能力:

  • 将主存看成是一个存储在磁盘上的高速缓存,在主存中只保存活动区域,并根据需要在磁盘和主存之间来回传送数据,通过这种方式,更高效地使用主存
  • 为每个进程提供一致的地址空间,从而简化存储器管理
  • 保护每个进程的地址空间不被其他进程破坏

由于进程拥有自己独占的虚拟地址空间,CPU通过地址翻译将虚拟地址转换成真实的物理地址,每个进程只能访问自己的地址空间。因此,在没有其他机制(进程间通信)的辅助下,进程之间是无法共享数据的。

进程各自持有一份数据,默认无法共享数据。默认的进程之间相互是独立,如果想让进程之间数据共享,就得有个特殊的数据结构,这个数据结构就可以理解为他有穿墙的功能 如果你能穿墙的话两边就都可以使用了

#!/usr/bin/env python
#coding:utf-8
from multiprocessing import Process
from multiprocessing import Manager
import time
li = []
def foo(i):
    li.append(i)
    print 'say hi',li
for i in range(10):
    p = Process(target=foo,args=(i,))
    p.start()
print 'ending',li

使用特殊的数据类型,来进行穿墙:

#通过特殊的数据结构:数组(Array)
from multiprocessing import Process,Array
#创建一个只包含数字类型的数组(python中叫列表)
#并且数组是不可变的,在C,或其他语言中,数组是不可变的,之后再python中数组(列表)是可以变得
#当然其他语言中也提供可变的数组
#在C语言中数组和字符串是一样的,如果定义一个列表,如果可以增加,那么我需要在你内存地址后面再开辟一块空间,那我给你预留多少呢?
#在python中的list可能用链表来做的,我记录了你前面和后面是谁。列表不是连续的,数组是连续的
'''
上面不是列表是“数组"数组是不可变的,附加内容是为了更好的理解数组!
'''
temp = Array('i', [11,22,33,44]) #这里的i是C语言中的数据结构,通过他来定义你要共享的内容的类型!点进去看~
def Foo(i):
    temp[i] = 100+i
    for item in temp:
        print i,'----->',item
for i in range(2):
    p = Process(target=Foo,args=(i,))
    p.start()
第二种方法:
#方法二:manage.dict()共享数据
from multiprocessing import Process,Manager  #这个特殊的数据类型Manager
manage = Manager()
dic = manage.dict() #这里调用的时候,使用字典,这个字典和咱们python使用方法是一样的!
def Foo(i):
    dic[i] = 100+i
    print dic.values()
for i in range(2):
    p = Process(target=Foo,args=(i,))
    p.start()
    p.join()

既然进程之间可以进行共享数据,如果多个进程同时修改这个数据是不是就会造成脏数据?是不是就得需要

进程的锁和线程的锁使用方式是非常一样的知识他们是用的类是在不同地方的。

进程池

进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供使用的进进程,那么程序就会等待,直到进程池中有可用进程为止。

进程池中有两个方法:

  • apply
  • apply_async
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from  multiprocessing import Process,Pool
import time
def Foo(i):
    time.sleep(2)
    return i+100
def Bar(arg):
    print arg
pool = Pool(5) #创建一个进程池
#print pool.apply(Foo,(1,))#去进程池里去申请一个进程去执行Foo方法
#print pool.apply_async(func =Foo, args=(1,)).get()
for i in range(10):
    pool.apply_async(func=Foo, args=(i,),callback=Bar)
print 'end'
pool.close()
pool.join()#进程池中进程执行完毕后再关闭,如果注释,那么程序直接关闭。
'''
apply 主动的去执行
pool.apply_async(func=Foo, args=(i,),callback=Bar) 相当于异步,当申请一个线程之后,执行FOO方法就不管了,执行完之后就在执行callback ,当你执行完之后,在执行一个方法告诉我执行完了
callback 有个函数,这个函数就是操作的Foo函数的返回值!
'''

进程的缺点

无法即时完成的任务带来大量的上下文切换代价与时间代价。

进程的上下文:当一个进程在执行时,CPU的所有寄存器中的值、进程的状态以及堆栈中的内容被称为该进程的上下文。

上下文切换:当内核需要切换到另一个进程时,它需要保存当前进程的所有状态,即保存当前进程的上下文,以便在再次执行该进程时,能够得到切换时的状态并执行下去。

线程

线程的定义

在计算中,进程是正在执行的计算机程序的一个实例。任何进程都有 3 个基本组成部分:

  • 一个可执行程序。
  • 程序所需的相关数据(变量、工作空间、缓冲区等)
  • 程序的执行上下文(进程状态)

线程是进程中可以调度执行的实体。此外,它是可以在 OS(操作系统)中执行的最小处理单元。

简而言之,线程是程序中的一系列此类指令,可以独立于其他代码执行。为简单起见,您可以假设线程只是进程的子集!

线程在线程控制块 (TCB)中包含所有这些信息:

  • 线程标识符:为每个新线程分配唯一 id (TID)
  • 堆栈指针:指向进程中线程的堆栈。堆栈包含线程范围内的局部变量。
  • 程序计数器:存放线程当前正在执行的指令地址的寄存器。
  • 线程状态:可以是running、ready、waiting、start或done。
  • 线程的寄存器集:分配给线程进行计算的寄存器。
  • 父进程指针:指向线程所在进程的进程控制块 (PCB) 的指针。

多线程被定义为处理器同时执行多个线程的能力。

在一个简单的单核 CPU 中,它是通过线程之间的频繁切换来实现的。这称为上下文切换。在上下文切换中,只要发生任何中断(由于 I/O
或手动设置),就会保存一个线程的状态并加载另一个线程的状态。上下文切换发生得如此频繁,以至于所有线程似乎都在并行运行(这被称为多任务)。

在 Python 中,threading模块提供了一个非常简单直观的 api,用于在程序中生成多个线程。

使用线程模块的简单示例

让我们考虑一个使用线程模块的简单示例:

# Python程序说明线程的概念
# 导入线程模块
import threading
def print_cube(num):
    """
    打印给定数字立方的函数
    """
    print("立方: {}".fORMat(num * num * num))
def print_square(num):
    """
    打印给定数字平方的函数
    """
    print("平方: {}".format(num * num))
if __name__ == "__main__":
    # creating thread
    t1 = threading.Thread(target=print_square, args=(10,))
    t2 = threading.Thread(target=print_cube, args=(10,))
    # starting thread 1
    t1.start()
    # starting thread 2
    t2.start()
    # 等到线程 1 完全执行
    t1.join()
    # 等到线程 2 完全执行
    t2.join()
    # 两个线程完全执行
    print("完成!")

平方: 100
立方: 1000
完成!

代码解析

让我们试着理解上面的代码:

  • 要导入线程模块,我们这样做:
import threading
  • 要创建一个新线程,我们创建一个Thread类的对象。它需要以下参数:
  • target : 线程要执行的函数
  • args:要传递给目标函数的参数

在上面的示例中,我们创建了 2 个具有不同目标函数的线程:

t1 = threading.Thread(target=print_square, args=(10,)) 
t2 = threading.Thread(target=print_cube, args=(10,))

要启动一个线程,我们使用 Thread 类的 start 方法。

t1.start() 
t2.start()

一旦线程启动,当前程序(你可以把它想象成一个主线程)也会继续执行。为了在线程完成之前停止当前程序的执行,我们使用join方法。

t1.join() 
t2.join()

结果,当前程序将首先等待 t1 的完成,然后 t2 。一旦它们完成,则执行当前程序的剩余语句。

协程

协程(Coroutine,又称微线程,纤程)是一种比线程更加轻量级的存在,协程不是被操作系统内核所管理,而完全是由程序所控制。

我们都熟悉函数,也称为子例程、过程、子过程等。函数是打包为一个单元以执行特定任务的指令序列。当一个复杂函数的逻辑被分成几个独立的步骤,这些步骤本身就是函数时,这些函数被称为辅助函数或子程序。

Python 中的子程序由负责协调这些子程序的使用的主函数调用。子程序只有一个入口点。 协程是子程序的泛化。它们用于协作式多任务处理,其中一个进程定期或在空闲时自愿放弃(放弃)控制权,以使多个应用程序能够同时运行。协程和子程序的区别是:

  • 与子程序不同,协程有许多用于暂停和恢复执行的入口点。协程可以暂停其执行并将控制权转移给其他协程,并且可以从中断点重新开始执行。
  • 与子程序不同,没有主函数可以按特定顺序调用协程并协调结果。协程是协作的,这意味着它们链接在一起形成管道。一个协程可能会使用输入数据并将其发送给其他处理它的协程。最后,可能会有一个协程来显示结果。

协程与线程

现在您可能在想协程与线程有何不同,两者似乎都在做同样的工作。
在线程的情况下,它是根据调度程序在线程之间切换的操作系统(或运行时环境)。而在协程的情况下,决定何时切换协程的是程序员编程语言。协程通过程序员在设定点暂停和恢复来协同工作多任务。

Python 协程

在 Python 中,协程类似于生成器,但几乎没有额外的方法,而且我们使用yield语句的方式也有细微的变化。生成器为迭代生成数据,而协程也可以使用数据
在 Python 2.5 中,引入了对 yield 语句的轻微修改,现在 yield 也可以用作表达式。例如在作业的右侧——

line = (yield)

我们发送给协程的任何值都会被(yield)表达式捕获并返回。

可以通过send()方法将值发送到协程。例如,考虑这个协程,它打印出带有前缀“Dear”的名称。我们将使用 send() 方法将名称发送到协程。

# 用于演示协程执行的 python3 程序
def print_name(prefix):
    print("Searching prefix:{}".format(prefix))
    while True:
        name = (yield)
        if prefix in name:
            print(name)
# 调用协程,什么都不会发生
corou = print_name("Dear")
# 这将开始执行协程并打印第一行 "Searching prefix..."
# 并将执行推进到第一个 yield 表达式
corou.__next__()
# 发送输入
corou.send("Haiyong")
corou.send("Dear Haiyong")

输出:

Searching prefix:Dear
Dear Haiyong

协程的执行

协程的执行类似于生成器。当我们调用协程时,什么都没有发生,它只在响应next()send ()方法时运行。在上面的例子中可以清楚地看到这一点,因为只有在调用__next__()方法之后,我们的协程才开始执行。在这个调用之后,执行前进到第一个 yield 表达式,现在执行暂停并等待值被发送到 corou 对象。当第一个值被发送给它时,它会检查前缀和打印名称(如果存在前缀)。打印完名称后,它会遍历循环,直到再次遇到name = (yield)表达式。

关闭协程

协程可能无限期运行,关闭协程使用close()方法。当协程关闭时,它会生成GeneratorExit异常,该异常可以以通常捕获的方式捕获。关闭协程后,如果我们尝试发送值,它将引发StopIteration异常。下面是一个简单的例子:

# Python3 program for demonstrating
# closing a coroutine
def print_name(prefix):
    print("Searching prefix:{}".format(prefix))
    try :
        while True:
                name = (yield)
                if prefix in name:
                    print(name)
    except GeneratorExit:
            print("关闭协程!!")
corou = print_name("Dear")
corou.__next__()
corou.send("Haiyong")
corou.send("Dear Haiyong")
corou.close()

输出:

搜索前缀:Dear 
Dear Haiyong
关闭协程!!

链接协程以创建管道

协程可用于设置管道。我们可以使用 send() 方法将协程链接在一起并通过管道推送数据。管道需要:

  • 初始源(生产者)派生整个管道。生产者通常不是协程,它只是一个简单的方法。
  • 一个 sink,它是管道的端点。接收器可能会收集所有数据并显示它。

以下是一个简单的链接示例

# 用于演示协程链接的 Python 程序

def producer(sentence, next_coroutine):
    '''
    producer 只是拆分字符串并将其
    提供给 pattern_filter 协程
    tokens = sentence.split(" ")
    for token in tokens:
        next_coroutine.send(token)
    next_coroutine.close()
def pattern_filter(pattern="ing", next_coroutine=None):
    在接收到的令牌中搜索模式,如果模式匹配,
    将其发送到 print_token() 协程进行打印
    print("Searching for {}".format(pattern))
    try:
        while True:
            token = (yield)
            if pattern in token:
                next_coroutine.send(token)
    except GeneratorExit:
        print("过滤完成!!")
def print_token():
    充当接收器,只需打印接收到的令牌
    print("我沉了,我会打印令牌")
            print(token)
        print("打印完成!")
pt = print_token()
pt.__next__()
pf = pattern_filter(next_coroutine = pt)
pf.__next__()
sentence = "Haiyong is running behind a fast moving car"
producer(sentence, pf)

输出:

我沉了,我会打印令牌
Searching for ing
running 
moving 
过滤完成!
打印完成!

总结

1.线程和协程推荐在 IO 密集型的任务(比如网络调用)中使用,而在CPU密集型的任务中,表现较差。
2.对于CPU密集型的任务,则需要多个进程,绕开GIL的限制,利用所有可用的CPU核心,提高效率。
3.在高并发下的最佳实践就是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。

  • CPU 密集型: 多进程
  • IO 密集型: 多线程(协程维护成本较高,而且在读写文件方面效率没有显著提升)
  • CPU 密集和 IO 密集: 多进程+协程

到此这篇关于Python 高级教程之线程进程和协程的代码解析的文章就介绍到这了,更多相关Python线程进程和协程内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Python高级教程之线程进程和协程的代码解析

本文链接: https://www.lsjlt.com/news/117620.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Python高级教程之线程进程和协程的代码解析
    目录进程进程 5 种基本状态进程的特点进程间数据共享进程池进程的缺点线程线程的定义使用线程模块的简单示例代码解析协程协程与线程Python 协程协程的执行关闭协程链接协程以创建管道总...
    99+
    2024-04-02
  • python教程之进程和线程
    目录进程和线程的区别和联系多进程线程池多线程总结进程和线程的区别和联系 终于开始加深难度,来到进程和线程的知识点~ 单就这两个概念,就难倒过不少初学者——今天...
    99+
    2024-04-02
  • Python的进程,线程和协程实例分析
    这篇“Python的进程,线程和协程实例分析”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Python的进程,线程和协程实例...
    99+
    2023-06-29
  • 实例详解Python的进程,线程和协程
    目录前言前提条件相关介绍实验环境进程多进程用进程池对多进程进行操作线程使用_thread模块实现使用 threading 模块实现协程使用asyncio模块实现总结前言 本文用Pyt...
    99+
    2024-04-02
  • Python的进程,线程和协程实例详解
    目录相关介绍实验环境进程多进程用进程池对多进程进行操作线程使用_thread模块实现使用 threading 模块实现协程使用asyncio模块实现总结相关介绍 Python是一种跨...
    99+
    2024-04-02
  • 如何理解Python进程、线程、协程
    本篇内容介绍了“如何理解Python进程、线程、协程”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!什么是进...
    99+
    2024-04-02
  • 深度解析Python线程和进程
    目录什么是进程什么是线程线程与进程的区别并行与并发Python中的多进程Python中进程操作线程Python的threading模块锁Lock:全局解释器锁(GIL)参考文章:什么...
    99+
    2024-04-02
  • java高并发之理解进程和线程
    目录进程线程进程与线程的一个简单解释总结进程 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。程序是指...
    99+
    2024-04-02
  • 一文搞懂Python中的进程,线程和协程
    目录1.什么是并发编程2.进程与多进程3.线程与多线程4.协程与多协程5.总结1.什么是并发编程 并发编程是实现多任务协同处理,改善系统性能的方式。Python中实现并发编程主要依靠...
    99+
    2024-04-02
  • Python 中的进程、线程、协程、同步
    进程和线程究竟是什么东西?传统网络服务模型是如何工作的?协程和线程的关系和区别有哪些?IO过程在什么时间发生?在刚刚结束的 PyCon2014 上海站,来自七牛云存储的 Python 高级工程师许智翔带来了关于 Python 的分享《Pyt...
    99+
    2023-01-31
    线程 进程 Python
  • 一篇文章带你了解Python的进程,线程和协程
    目录线程线程锁threading.RLock和threading.Lock 的区别threading.Eventthreading.Conditionqueue 队列生产者消费者模型...
    99+
    2024-04-02
  • 掌握 Python 并发编程:揭秘协程、线程和进程
    并发编程是编写同时执行多个任务的代码的艺术,在 Python 中提供了协程、线程和进程等多种选项。了解这些选项及其适用场景对于开发高效、可扩展的应用程序至关重要。 协程 协程是 Python 中一种轻量级的并发机制,允许一个函数暂停执行...
    99+
    2024-02-18
    协程 线程 进程 并发 Python
  • Python进阶之协程详解
    目录协程协程的应用场景抢占式调度的缺点用户态协同调度的优势协程的运行原理Python 中的协程总结协程 协程(co-routine,又称微线程)是一种多方协同的工作方式。当前执行者在...
    99+
    2024-04-02
  • 详解Python中的进程和线程
    进程是什么? 进程就是一个程序在一个数据集上的一次动态执行过程。进程一般由程序、数据集、进程控制块三部分组成。我们编写的程序用来描述进程要完成哪些功能以及如何完成;数据集则是程序在执...
    99+
    2024-04-02
  • 对比Golang协程和线程的分析
    Golang协程与线程的差异解析 在现代编程语言中,多线程并发已经成为一种常见的编程模式,用于提高程序的性能和响应能力。然而,线程的创建和管理往往需要消耗大量的系统资源,同时在编程复杂性和错误处理上也存在一些...
    99+
    2024-01-24
  • python进阶之协程你了解吗
    目录协程的定义协程和线程差异协程的标准协程的优点协程的缺点python中实现协程的方式async&await关键字事件循环协程函数和协程对象awaitTask对象asynci...
    99+
    2024-04-02
  • Python Pandas高级教程之时间处理
    目录简介 时间分类 Timestamp DatetimeIndex date_range 和 bdate_range origin 格式化 Period DateOffset 作为i...
    99+
    2024-04-02
  • python协程和线程的差异有哪些
    小编给大家分享一下python协程和线程的差异有哪些,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!python是什么意思Python是一种跨平台的、具有解释性、编...
    99+
    2023-06-15
  • Python中多线程、多进程、协程的区别是什么
    今天就跟大家聊聊有关Python中多线程、多进程、协程的区别是什么,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。首先我们写一个简化的爬虫,对各个功能细分,有意识进行函数式编程。下面代...
    99+
    2023-06-16
  • Python编程实践操作教程:两数之和—Java和Python的代码对比
    Python编程实践操作教程:两数之和,分别用Java和Python表现!伙伴们可以做个对比!题目:给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。你可以假设每种输...
    99+
    2023-06-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作