【问题标题】:Nim: Spawned Function Cannot Have a Var Parameter, but Argument to acquire Must Be VarNim:生成的函数不能有 Var 参数,但要获取的参数必须是 Var
【发布时间】:2021-05-07 18:57:52
【问题描述】:

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

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

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

【问题讨论】:

  • 全局锁有什么问题?

标签: multithreading mutex nim-lang


【解决方案1】:

最干净的实现方式是什么?

使用全局锁。确实,当全局变量减少封装并使代码更难推理时,它们被认为是糟糕的风格,但像 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

【讨论】:

  • 非常感谢,非常全面!我还有几个问题。为什么使用全局锁更好?我看到很多人说要避免使用全局变量,我明白为什么。线程同步结构有什么不同?您写道“线程改变参数本质上是不安全的”。这比改变全局变量的线程更糟糕吗?
  • 很高兴我能帮上忙。是的,“被认为有害的全局变量”是传统智慧,但恕我直言,应该对锁、通道、线程对象等全局对象进行例外处理。这适用于 Nim 代码,所以我会更新我的帖子来讨论原因。
  • ref 也可以这样做吗?
  • 好问题:不。 (除非使用 gc:arc/orc)我希望我已经在上面解释过了,如果有什么我可以澄清的,请告诉我:-)
猜你喜欢
  • 2014-02-17
  • 2012-07-26
  • 1970-01-01
  • 2021-09-27
  • 2021-01-29
  • 2015-10-21
  • 1970-01-01
  • 2015-02-22
  • 1970-01-01
相关资源
最近更新 更多