【问题标题】:What is the difference between message-passing and method-invocation?消息传递和方法调用有什么区别?
【发布时间】:2011-04-03 11:59:55
【问题描述】:

消息传递和方法调用之间有区别吗,或者它们可以被认为是等价的吗?这可能是特定于语言的;许多语言不支持消息传递(尽管我能想到的所有语言都支持方法),而那些支持的语言可以有完全不同的实现。此外,根据语言(C 与 Java 与 Lisp 与您最喜欢的语言)的不同,方法调用也存在很大差异。我相信这是与语言无关的。使用传递的方法可以做什么,而调用的方法无法做到这一点,反之亦然(在您最喜欢的语言中)?

【问题讨论】:

  • 你的意思是“你能用传递的-消息做什么,你不能做......”?如果是这样,我会为您更正。
  • 面向对象编程的最初概念是“消息”在对象之间传递。如果你想Object2 执行一些操作,你可以向它传递一条消息,说“do SomeAction。在编程语言中,它的语法变成了Object2.SomeAction(...);然后它被称为“调用方法”,而不是“传递消息”。几乎所有语言的一个缺点是Object2.SomeAction(...) 既是同步的,又是几乎总是局限于同一个线程或进程。

标签: language-agnostic message-passing method-invocation


【解决方案1】:

在实践中它们确实不是一回事。消息传递是一种在两个或多个并行进程之间传输数据和指令的方法。方法调用是调用子程序的一种方式。 Erlang 的并发是建立在前者的概念之上的,它的面向并发编程。

消息传递很可能涉及某种形式的方法调用,但方法调用不一定涉及消息传递。如果是这样,那将是消息传递。消息传递是在并行进程之间执行同步的一种形式。方法调用通常意味着同步活动。调用者等待方法完成,然后才能继续。消息传递是协程的一种形式。方法调用是子程序的一种形式。

所有子程序都是协程,但所有协程都不是子程序。

【讨论】:

  • “消息传递”也可以指调用 Smalltalk、Objective-C 和 Ruby 等语言中的方法的方式。
  • 是的,这些语言喜欢将其视为消息传递,但它是一个子例程,涉及在暂停调用子例程的方法时将某些内容压入堆栈。该实现是堆栈传递的方法调用,这意味着调用者在等待调用例程返回时被阻塞。消息传递没有这样的语义。它不必等待其调用完成的例程是我的观点。这就是区别。
  • IMO OP 并没有真正讨论进程间通信。
  • 我也不是。我没有专门谈论 IPC。我以通用方式使用进程来表示在操作系统进程中运行的任何任务。但是在一个进程中,您仍然必须在并行任务之间传递信息。碰巧这两种方法也可以用于 IPC,但严格来说不是 IPC。
【解决方案2】:

使用Objective-C作为消息的例子和Java作为方法的例子,主要区别在于当你传递消息时,对象决定它要如何处理该消息(通常会导致调用对象中的实例方法) .

然而,在 Java 中,方法调用是一个更静态的事情,因为您必须引用您正在调用该方法的类型的 Object,并且该类型中必须存在具有相同名称和类型签名的方法,否则编译器会抱怨。有趣的是实际调用是动态的,尽管这对程序员来说并不明显。

例如,考虑一个类,例如

class MyClass {
    void doSomething() {}
}

class AnotherClass {
    void someMethod() {
        Object object = new Object();
        object.doSomething(); // compiler checks and complains that Object contains no such method.

        // However, through an explicit cast, you can calm the compiler down,
        // even though your program will crash at runtime
        ((MyClass) object).doSomething(); // syntactically valid, yet incorrect
    }
}

然而,在 Objective-C 中,编译器只会向您发出警告,告知您将消息传递给它认为该对象可能无法理解的对象,但忽略它并不会阻止您的程序执行。

虽然这非常强大和灵活,但如果由于堆栈损坏而使用不当,可能会导致难以发现的错误。

改编自文章here。 另请参阅this 文章了解更多信息。

【讨论】:

    【解决方案3】:

    IIRC,它们已被正式证明是等效的。至少表明它们应该是不需要太多的思考。所需要的只是暂时忽略被调用地址与内存中实际位置的直接等价性,并将其简单地视为一个数字。从这个角度来看,数字只是一个抽象标识符,用于唯一标识您希望调用的特定类型的功能。

    即使您在同一台机器上调用函数,也没有真正要求被调用地址直接指定被调用函数的物理(甚至虚拟)地址。例如,尽管几乎没有人真正使用过它们,但英特尔保护模式任务门允许直接调用任务门本身。在这种情况下,只有地址的段部分被视为实际地址——即,对任务门段的任何调用最终都会调用相同的地址,而不管指定的偏移量如何。如果需要,处理代码可以检查指定的偏移量,并使用它来决定要调用的单个方法——但是指定的偏移量和被调用函数的地址之间的关系可以是完全任意的。

    成员函数调用只是一种消息传递类型,它在所讨论服务的客户端和服务器共享公共地址空间的常见情况下提供(或至少有助于)优化。抽象服务标识符和该服务提供者所在地址之间的 1:1 对应关系允许从一个到另一个的微不足道的、异常快速的映射。

    同时,请不要误会:看起来类似于成员函数调用的事实并不能阻止它在另一台机器上实际执行或异步执行,或(经常)两者兼而有之.实现这一点的典型机制是代理函数,它将成员函数调用的“虚拟消息”转换为可以(例如)根据需要通过网络传输的“真实消息”(例如,Microsoft 的 DCOM 和 CORBA 都可以)这很正常)。

    【讨论】:

      【解决方案4】:

      作为第一个近似值,答案是:没有,只要你“表现正常”

      尽管许多人认为存在 - 从技术上讲,它通常是相同的:对要为特定命名操作执行的一段代码的缓存查找(至少对于正常情况)。将操作的名称称为“消息”或“虚拟方法”没有区别。

      但是:Actor 语言确实不同:在拥有活动对象时(每个对象都有一个隐式消息队列和一个工作线程 - 至少在概念上是这样),并行处理变得更容易处理(谷歌也为更多)。

      但是:在 Smalltalk 中,可以包装对象以使其像演员一样,而无需实际更改编译器、语法甚至重新编译。

      但是:在 Smalltalk 中,当您尝试发送接收者无法理解的消息(即“someObject foo:arg”)时,会创建一个消息对象,其中包含名称和参数,以及该消息 -对象作为参数传递给“doesNotUnderstand”消息。因此,对象可以自行决定如何处理未实现的消息发送(也称为未实现方法的调用)。它可以 - 当然 - 将它们推入队列中,以便工作进程对它们进行排序......

      当然,这对于静态类型语言来说是不可能的(除非你大量使用反射),但实际上这是一个非常有用的特性。代理对象、按需加载代码、远程过程调用、学习和自我修改代码、适应和自我优化程序、corba 和 dcom 包装器、工作队列都建立在该方案之上。它可能被滥用,并导致运行时错误 - 当然。 所以它是一把双面剑。锋利而强大,但在初学者手中很危险......

      编辑:我在这里写关于语言实现的文章(如 Java 与 Smalltalk - 不是进程间机制。

      【讨论】:

        【解决方案5】:

        消息传递和方法调用之间有区别吗,或者它们可以被认为是等价的吗?

        它们很相似。一些区别:

        消息可以同步或异步传递(例如Windows中SendMessage和PostMessage的区别)

        您可能在不确切知道要发送到哪个远程对象的情况下发送消息

        目标对象可能在远程机器或操作系统上。

        【讨论】:

        • 很抱歉很久以前就找到了答案,但关于“目标对象可能在远程机器或操作系统上”——这对于消息传递或方法调用来说真的是独一无二的?如果我正确理解了您的含义,那么您将其说明为消息传递的属性。如果是这样,Java 中的远程方法调用与此不矛盾吗?
        猜你喜欢
        • 2021-01-29
        • 2016-09-17
        • 2015-03-22
        • 1970-01-01
        • 2011-04-09
        • 1970-01-01
        • 2012-11-27
        • 2010-12-23
        • 1970-01-01
        相关资源
        最近更新 更多