问题描述
我一直在 Nim 中使用 threadpool
并且遇到了 spawn
ed 函数不能接受可变参数的要求。但是,我想根据 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 对象(任何包含 ref
、string
或 seq
的对象)不能在线程之间传递,即使是全局对象或包装在 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