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

Nim: Spawned Function Cannot Have a Var Parameter, but Argument to acquire Must Be Var

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

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

import os, threadpool, locks

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

var lock: Lock
initLock lock

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

sync()
deinitLock lock

What would the cleanest way to implement this be?

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

Is it with good reason that I can't do what I want?

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

Why is that any different from mutating a global?

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

Each thread has its own (garbage collected) heap, and sharing of memory is restricted to global variables. This helps to prevent race conditions. GC efficiency is improved quite a lot, because the GC never has to stop other threads and see what they reference.

这也意味着 GC 对象(任何包含 refstringseq 的对象)不能在线程之间传递,即使是全局对象或包装在 Channel 中也是如此或共享列表。
引用自 passing channels safely:

Note that when passing objects to procedures on another thread by pointer (for example through a thread's argument), objects created using the default allocator will use thread-local, GC-managed memory. Thus it is generally safer to store channel objects in global variables (as in the above example), in which case they will use a process-wide (thread-safe) shared heap.

However, it is possible to manually allocate shared memory for channels using e.g. system.allocShared0 and pass these pointers through thread arguments

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

pointers aren't really first-class objects...no way around dereferencing pointers..Have I just missed the dereference operator?

虽然 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