Programming languages 为什么闭包突然对优化在多核上运行的程序有用?

Programming languages 为什么闭包突然对优化在多核上运行的程序有用?,programming-languages,concurrency,closures,multicore,Programming Languages,Concurrency,Closures,Multicore,我读过一篇文章,声称闭包(或“块”)是“多核战争”中的有用武器,因为 […]它们允许您创建 工作,每个人都有自己的副本 不要踩到每一个 其他人也因此失去了信心。更重要的是, 你可以像这样传递这些单位 实际上,它们是价值观 它们包含一整堆值 (双关语),以及用于 执行一些操作 现在,我不是在讨论闭包的有用性,也可能是在共享内存模型中的并发编程,但是一个只作用于本地数据(或进程,或参与者,或…)的线程有什么区别 闭包本身对于并发编程来说,不是和没有调度器的线程一样有用吗 有非局部副作用的闭包怎么办?

我读过一篇文章,声称闭包(或“块”)是“多核战争”中的有用武器,因为

[…]它们允许您创建 工作,每个人都有自己的副本 不要踩到每一个 其他人也因此失去了信心。更重要的是, 你可以像这样传递这些单位 实际上,它们是价值观 它们包含一整堆值 (双关语),以及用于 执行一些操作

现在,我不是在讨论闭包的有用性,也可能是在共享内存模型中的并发编程,但是一个只作用于本地数据(或进程,或参与者,或…)的线程有什么区别

闭包本身对于并发编程来说,不是和没有调度器的线程一样有用吗


有非局部副作用的闭包怎么办?

这里有一个很好的闭包定义:

“闭包”是一个表达式 (通常是一个函数)可以 自由变量和 绑定这些变量的环境 (即“关闭”表达式)

我认为您混淆了定义,例如,在javascript中,当我更改DOM时,我的闭包通常会产生非局部的副作用

闭包非常有用,这就是为什么C#将闭包添加到语言中的原因

在函数式编程语言等语言中,它们似乎不一定创建线程(由于上下文切换,您必须为此付出代价),而是创建轻量级进程。框架或编译器将控制要创建的内容,以确保处理器得到最佳利用

是否使用闭包编写不如使用不可变数据重要

例如,如果我有一个没有全局数据的应用程序,但每个线程都使用自己的本地副本,那么由操作系统和调度程序决定我的应用程序将使用哪些内核。不幸的是,在C/C++中,编译器似乎不知道如何做好这项工作,因此,通过迁移到FP,我们可以使用诸如Erlang之类的框架,这些框架已经处理分布式处理很长时间了,并且可以利用他们的经验


在Erlang之类的环境中,actor的开销比C/C++线程小,因为actor的切换速度似乎更快。

原因在于,在编程语言中使用闭包可以更容易地在另一个线程中完成某些工作。我认为作者应该在那个论点中提到高阶函数的重要性

我最喜欢的高阶函数介绍是,我不会在这里介绍一个糟糕的复制品

因此,如果要在for循环中执行闭包,那么使用闭包并不能免费提供并行性,例如

for (int i = 0; i < numElements; i++) {
  result[i] = closure(inputs[i], i);
}
在这些语言中,闭包消除了为短代码段在某处声明新函数的痛苦。这使得使用高阶函数更有趣

下面的Haskell代码定义了一个函数
f
,该函数获取一个数字列表,并返回一个列表,其中每个输入
i
被替换为
2i+1
。通过省去创建函数来计算
2i+1
的麻烦,这是1行代码,而不是2行代码

f nums = map (\i -> 2*i+1) nums

再次,请参阅,以获取有关如何扩展到真正的代码基础的有力论据。

这篇文章中的这句话将许多误解归纳为一个句子片段:

[…]它们允许您创建单位 每个人都有自己的工作 复制堆栈,不要踩到 结果是,彼此的脚趾

对于带有闭包的语言,这通常是不正确的

首先,为了提高效率,它们更多地引用堆栈,而不是副本。在大多数语言中,您可以通过引用修改内容。因此,在这些语言中,此功能根本不提供隔离工作单元的方法。如果有什么不同的话,它会让人更加困惑

其次,在大多数(理智的)语言中,您可以引用任何在词汇上包含局部函数的东西。不能仅引用堆栈上的任何位置。例如,您无法深入到调用函数的函数的局部变量中。。。等等。您只能访问本地声明的函数的变量/参数,这些函数的文本包含使用该函数的文本。此外,局部变量和参数(“在堆栈上”)并不是唯一在词汇上包含函数的东西。因此,在这里调用“堆栈”是错误的概念

Java就是这样一种语言,它只能在“匿名内部类”闭包中复制局部变量和参数。但是,在外部类的
引用的情况下,它仍将采用引用

在关闭
this
的情况下,内部类现在将存储对外部类的隐式引用-实际上与堆栈无关

C#中的情况类似,只是局部变量和参数通过引用而不是复制来捕获

var counter = 0;
Repeat(10, () => counter++);
假设
Repeat
是一个库函数,它启动另一个线程,现在将每隔10毫秒调用传递给它的
操作
lambda。希望你能看到这是一个非常简洁的创造比赛条件的方法


唯一能避免这个问题的语言是像Haskell这样的纯函数式语言,但这显然不是因为闭包,而是因为您永远不能修改任何共享状态。(Haskell仍然无法完全避免这个问题;大多数真正的软件都必须在某个时刻与程序外部的共享状态交互,Haskell的标准库有一种方法可以做到这一点)。

这篇文章似乎在改变闭包的定义,使其具有某种意义
var counter = 0;
Repeat(10, () => counter++);