Nim:生成的函数不能有 Var 参数,但获取的参数必须是 Var

问题描述

我一直在 Nim 中使用 threadpool 并且遇到了 spawned 函数不能接受可变参数的要求。但是,我想根据 Lock 的类型传递一个 acquire,而后者又必须 是可变的。我发现解决这个问题的唯一方法是让锁可变并在全局范围内声明,这样我就不必将它传递给函数 I spawn

但我真的宁愿避免这种情况。我有使用指针的想法 - 所以锁可以是可变的,但指针本身不是 - 来解决这个问题,但看起来指针并不是 Nim 中真正的一流对象。我只是尝试将 waitLock 的参数声明为 ref(第 3 行),但我仍然收到抱怨 acquire 必须传递 var Lock 而不是 {{ 1}} 在这里。而且看起来取消引用指针也是自动完成的,所以没有办法解决它......?有没有办法绕过使用动态范围并将锁显式传递到 proc 中?我不能做我想做的事是否有充分的理由?或者我刚刚错过了某些手册中的取消引用运算符?最简洁的实现方式是什么?

ref Lock

解决方法

最简洁的实现方式是什么?

使用全局锁。确实,当全局变量减少封装并使代码更难推理时,它们被认为是糟糕的风格,但是像 Channels、Locks 和 Thread 对象这样的东西在语义上是全局的,所以恕我直言,这些批评并不适用

我不能做我想做的事是否有充分的理由?

是的。线程改变参数本质上是不安全的,因此 Nim 通常禁止将 var 参数传递给线程是合适的。

为什么这与改变全局变量有什么不同?

Nim 的 memory model 有点不同。引用手册:

每个线程都有自己的(垃圾回收)堆,内存共享仅限于全局变量。这有助于防止竞争条件。 GC 效率提高了很多,因为 GC 永远不必停止其他线程并查看它们引用的内容。

这也意味着 GC 对象(任何包含 refstringseq 的对象)不能在线程之间传递,即使是全局对象或包装在 Channel 或 SharedList 中.
引自passing channels safely

请注意,当通过指针(例如通过线程的参数)将对象传递给另一个线程上的过程时,使用默认分配器创建的对象将使用线程本地、GC 管理的内存。因此,将通道对象存储在全局变量中通常更安全(如上例所示),在这种情况下,它们将使用进程范围(线程安全)共享堆。

但是,可以使用例如手动为通道分配共享内存。 system.allocShared0 并通过线程参数传递这些指针

使用 --gc:orc/--gc:arc 时取消了此限制,新的 Isolated 允许在线程之间安全无复制地移动子图。使用此机制的 Channels 的新实现将在下一个版本中(无论 1.4.6 之后的版本)

指针并不是真正的一流对象...没有办法解引用指针..我刚刚错过了解引用运算符吗?

虽然 Nim 鼓励使用 ref(跟踪引用)以确保安全和方便,但作为一种系统语言,课程指针(未跟踪引用)得到完全支持。

要从可变对象中获取未跟踪的引用,请使用 addr,取消引用 ptr 的语法与 ref 相同:[]

您忽略的手册部分是 here

这是您示例中的语法:

import os,threadpool,locks

proc waitLock(lock: ptr Lock): void =
  acquire lock[]
  echo "Child thread has lock!"
  release lock[]

var lock: Lock
initLock lock

spawn waitLock(lock.addr)
acquire lock
echo "Parent thread has lock!"
release lock

sync()
deinitLock lock