子程序,协同程序,功能和线程之间的区别?

Div*_*mmy 23 terminology

从过去2天开始,我一直在阅读编程艺术,第1卷.

有一个关于子程序和协同例程的主题.我感到困惑,当书中提到关于协同程序在main()之后初始化协同程序时,我不能理解,与子程序不同,被调用的协同例程成为调用协同例程的子例程.

我试着在这里研究之前提出的问题,在同一个环境中我也开始了解函数和线程的概念.本书给出了一个关于使用MIX计算机模型的协同例程的例子.我可以使用任何简单的伪代码,让我理解,在高层语言的当前语境中使用一个关于协同例程和其他术语的例子吗?

那么,一个联合问题如何区分子程序,协同例程,函数和线程?

rsk*_*kar 33

直到我们从真正知道的人那里得到一个帖子,这是我对这个问题的理解,FWIW.

子程序和函数本质上是相同的,但有一点不同:函数返回某种值(通常通过堆栈或CPU寄存器),而子程序则不然.无论是子程序还是函数,它都是一块可执行代码,只有一个入口点.协同例程也是一个可执行代码块,就像子例程一样,它有一个入口点.但是,它也有一个或多个重返点.稍后会详细介绍.

在进入线程之前,让我们回顾一下:计算机程序(也称为进程)通常将其内存分配组织到代码空间,堆和堆栈中.代码空间存储其可执行代码的一个或多个块.堆栈存储子程序,函数和协同例程(以及其他事项)的参数,自动变量和返回地址.堆是进程可用的广泛内存空间,无论其用途如何.除了这些存储空间之外,还有CPU寄存器,每个寄存器存储一组位.这些位可以是整数值,存储器地址,一堆状态标志等等.大多数程序员不需要了解它们,但它们在那里并且对CPU的操作至关重要.可能值得了解的是程序计数器,堆栈指针和状态寄存器,但我们不打算在这里介绍它们.

线程是单个逻辑执行流程.在原始计算系统中,只有一个线程可用于进程.在现代计算系统中,进程由一个或多个线程组成.每个线程都有自己的堆栈和一组CPU寄存器(这在物理上是不可能的,但在逻辑上是虚拟的 - 我们将在这里详细介绍一个细节).但是,虽然进程的每个线程都有自己的堆栈和寄存器,但它们都将共享相同的堆和代码空间.它们(可能)同时运行; 可以在多核CPU中真正发生的事情.因此,程序的两个或多个部分可以同时运行.

回到协同例程:如前所述,它有一个或多个重新进入点.重新进入点意味着协同例程可以允许其他一些代码块在其自身之外具有一些执行时间,然后在将来某个时间将执行时间恢复到其自己的代码块内.这意味着只要执行到外部代码块然后返回到协同例程的代码块,就会保留(并在需要时恢复)协同例程的参数和自动变量.虽然常见于许多汇编语言,但协同例程并不是直接在每种编程语言中实现的.在任何情况下,都可以以概念的方式实现协同例程.在http://en.wikipedia.org/wiki/Coroutine上有一篇关于协同例程的好文章.

在我看来,实现协同常规设计模式有两个主要动机:(1)克服单线程流程的局限性; (2)希望实现更好的计算性能.动机(1)很清楚,当过程必须同时解决许多事情时,单个线程是必须的.动机(2)可能不太清楚,因为它与系统硬件,编译器设计和语言设计的许多细节有关.我只能想象,通过减少堆栈操作,避免在子例程中重做初始化或减轻维护多线程进程的一些开销,可以减少计算工作量.

HTH

  • 例如,协同例程也是 Python 中的[生成器](https://wiki.python.org/moin/Generators) 的基础。有效构建长度为 N 且空间复杂度为 O(1) 的迭代。 (3认同)

and*_*oot 26

专注于coroutine vs子程序:

协同程序可以产生,这很有趣.

产量'记住'合作例程在何时再次被调用它将在它停止的地方继续.

例如:

  coroutine foo {
    yield 1;
    yield 2;
    yield 3;
  }
  print foo();
  print foo();
  print foo();
Run Code Online (Sandbox Code Playgroud)

印刷品:1 2 3

注意:协同程序可以使用返回,并且行为就像子例程一样

  coroutine foo {
    return 1;
    return 2; //Dead code
    return 3;
  }
  print foo();
  print foo();
  print foo();
Run Code Online (Sandbox Code Playgroud)

印刷品:1 1 1

  • 一如既往,示例价值超过千字:) (12认同)

art*_*ter 5

我想扩展现有的答案,添加以下内容:
在调用一段代码时存在 4 个主要概念:

  1. 创建上下文(又名“框架”,此代码运行的本地环境)
  2. 恢复/调用上下文(将控制权转移到该帧,也就是跳转)
  3. 分离(恢复另一个上下文,类似于 2。)
  4. 和上下文的破坏

在子例程中,创建和恢复与“调用”指令同时发生 - 分配堆栈帧,推送参数和返回地址,执行跳转到被调用的代码段。此外,分离(恢复调用者)和销毁与“返回”指令同时完成 - 堆栈帧被取消分配,控制权转移到调用者(通过先前提供的返回地址),调用者被留下来清理垃圾一个堆栈来挑选返回值(取决于您的调用约定)。
在协程中,这些主要概念独立存在,彼此解耦。您在某个时间创建了一个协程,然后您可以稍后将控制权转移给它(因此它可以为您带来一些结果,可能多次),然后您可以在稍后的某个时间点销毁它。