【问题标题】:Call by value result or something else按值结果或其他方式调用
【发布时间】:2014-10-17 14:18:59
【问题描述】:

在编程语言课程中,我的教授举了一个例子。

假设如下代码:

int x=initialization();
Thread T1=new computethread(x);
Thread t2=new evaluatethread(x);
...
...
Thread t100=new evaluatethread(x);

线程t1t100需要变量x的初始值进行计算,t1需要更改xcomputethreadevaluatethread的参数传递是首选?

我的教授说:

1) call by value result

2) call by ref

首选将x发送到computethreadevaluatethread,为什么按结果调用或按名称调用或此顺序如2)按值结果调用,1)按引用调用不是首选? 为什么他认为这些更好?

【问题讨论】:

  • 你能把computethreadevaluatethread之间的区别弄得更清楚吗?你的意思是computethreadx是一个输入/输出参数,而evaluatethread它只是一个输入?
  • 他说,t1到t100需要x的初始值,t1改变x的值。我认为只是 computethread() 改变了 x 的值,其他的没有改变。

标签: java compiler-construction operating-system thread-safety programming-languages


【解决方案1】:

Java中只有一种的传输方式:传值。

像 int 这样的基元。 double、boolean 很明显:值被传递。

堆上的对象也是如此,但按值传递的东西是对该对象的引用。对象本身在堆上,而不是在栈帧上。 Java 中没有像 C++ 那样的内置复制机制。

您无法更改引用的值,但如果它所指向的对象暴露了可变数据,则您可以修改它的状态。

【讨论】:

  • 亲爱的 duffymo,我不明白您如何验证我的教授解决方案?
  • @Dr.Jackson 我认为,duffymo 说这个问题没有多大意义......因为在 Java 中,只有传递变量的方法。所以至少可以说给出两个选项看起来很奇怪。
  • 我不知道你的教授在想什么,但我的答案是正确的。 James Gosling 在这里解释:c2.com/cgi/wiki?JavaPassesByValue
【解决方案2】:

我认为您的教授可能使用看起来像 Java 的语法作为示例,但正如其他人所指出的,Java 仅支持按值传递。但是,如果我们讨论的是一种语法极其相似的假设语言,那么 pass-by-value-result 描述了所需的语义......

在传值结果下,对参数的修改只有在函数完成后才会提交。在函数完成之前,只对本地副本进行修改。

进一步说明,pass-by-value-result大致相当于按值提供所有输入参数,并返回一个封装了所有参数更新值的对象。调用函数后,调用者中的变量会被赋予这些更新后的值。相比之下,在按值传递中,更新的值被忽略并丢弃,而在按引用传递中,更新立即分配回原始源。所以,为了更具体一点,让我们考虑这个例子:

add(x, value):
  print 'Called with: ' + x
  x = x + value
  print 'Modified x: ' + x

main():
  x = 0
  add(x, 3)
  add(x, 2)
  print 'Caller x: ' + x

在按值传递下,这将打印:

Called with 0
Modified x: 3
Called with 0
Modified x: 2
Caller x: 0

... 因为参数是“add”中的一个副本,并且对该副本的更新不会传播回调用者。在 pass-by-value-result 下,此示例的输出将是:

Called with 0
Modified x: 0
Called with 3
Modified x: 3
Caller x: 5

...因为,“add”函数传递了“x”的不可变副本,并且写入“x”仅在函数退出后传播(因此,在“add”中,后续尝试读取该值给出原始值而不是修改后的值)。但是,在函数完成后,分配的值将返回/提交给原始变量。相比之下,按引用传递会输出:

Called with 0
Modified x: 3
Called with 3
Modified x: 5
Caller x: 5

...因为传递引用将变量“x”的内存地址传递给“add”函数,“add”函数立即将修改写回该位置。

【讨论】:

  • 为什么不首选按名称调用或按结果调用?你能补充一点细节吗/
  • 你的意思是因为computethread改变了x的值,而其他线程需要x的初始值,我们应该为computethread选择传值结果?
  • Pass-by-result 只是按值结果的简写,AFAIK。我已经更新了答案以更清楚地解释这一点。 Pass-by-name 更加奇特,更像是 C 和 C++ 中的宏的工作方式。 pass-by-name 的主要作用是参数的文本(包括整个表达式)被替换和惰性求值。在这里解释得更好:cs.sfu.ca/~cameron/Teaching/383/PassByName.html
  • @Dr.Jackson 是的,没错。由于这些需要初始值,因此使用 pass-by-value-result 将使它们看到原始值,同时仍然允许计算线程更新参数。
  • 我的最后一个问题是 1)为什么其他人使用 ref 调用? 2)在线程完成后通过值传递x的结果值改变?谢谢你的时间。谢谢。再次感谢。
【解决方案3】:

如果我做出的假设是正确的,我认为这很可能并且忽略了与 IMO 问题无关的语言。

期望的行为是让一个线程修改x,另外还有 99 个线程只对其初始值执行一些计算。

请记住,线程共享相同的地址空间。因此,为了避免线程之间的干扰,您希望它们对参数的本地副本进行操作。所以引用调用是没有问题的(下面提到了一个“特殊”情况)。

现在因为evaluatethreads 不需要返回任何东西,所以他们可以使用按值调用。 computethread需要修改值,所以需要call-by-value-return

另请注意,排除干扰的所有变体也是有效的。例如,如果你pass-by-reference 只对t1,那很好,因为没有其他线程会直接接触x。但这只有在您可以保证它是唯一线程的情况下才可以。一般来说,我不会称其为安全假设。

按名称调用不好,因为如果computethreadevaluatethread 需要它的值之前修改x,则初始化后它不会得到x,而是更新后的。

【讨论】:

  • 亲爱的@Luk,按值调用返回是按值调用结果?
  • 可能是ref调用速度快,computethread而不是value调用,使用call by ref?
  • Wikipedia says: "Call-by-copy-restore、copy-in-copy-out、call-by-value-result 或 call-by-value-return(如在Fortran 社区)是按引用调用的一种特殊情况,其中提供的引用对调用者来说是唯一的。”,所以我想它是一样的。按值调用会生成副本,而按引用调用则不会。制作副本可能很昂贵。这就是为什么在 c++ 中引入了 const 引用。约定不修改,但跳过按值传递的复制开销。
  • 但是......在这种情况下没有优势,引用通常具有整数大小(至少在“接近机器”的语言中),即使不是,它们也不应该很大足以证明(非常量)参考放弃安全性。当然这是我的看法。
  • 和你一起回答我确定对于计算线程我们使用按值调用结果,但对于评估线程我有疑问。
猜你喜欢
  • 1970-01-01
  • 2014-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-01
  • 1970-01-01
  • 2020-01-19
  • 1970-01-01
相关资源
最近更新 更多