Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/list/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Concurrency Nim线程间消息传递:如何避免全局TChannel?_Concurrency_Nim Lang - Fatal编程技术网

Concurrency Nim线程间消息传递:如何避免全局TChannel?

Concurrency Nim线程间消息传递:如何避免全局TChannel?,concurrency,nim-lang,Concurrency,Nim Lang,我有一个线程间通信问题的简单示例:我想在后台线程中运行任意“随时”算法。anytime算法以增量方式执行一些结果类型T的计算,也就是说,它偶尔会生成更新、更精确的结果。用Nim的说法,它们可能最好用迭代器来表示。在主线程中,我现在希望将这样的迭代器分别封装在自己的线程中,可以查询线程中的“是否有新值可用”或“当前计算结果是什么” 由于我不熟悉Nim的并发性概念,因此难以实现所需的线程间通信。我的想法是使用t频道进行通信。根据,一个TChannel不能与spawn结合使用,但需要使用createT

我有一个线程间通信问题的简单示例:我想在后台线程中运行任意“随时”算法。anytime算法以增量方式执行一些结果类型
T
的计算,也就是说,它偶尔会生成更新、更精确的结果。用Nim的说法,它们可能最好用迭代器来表示。在主线程中,我现在希望将这样的迭代器分别封装在自己的线程中,可以查询线程中的“是否有新值可用”或“当前计算结果是什么”

由于我不熟悉Nim的并发性概念,因此难以实现所需的线程间通信。我的想法是使用
t频道进行通信。根据,一个
TChannel
不能与
spawn
结合使用,但需要使用
createThread
。我设法编译并运行了以下内容:

import os, threadpool

proc spawnBackgroundJob[T](f: iterator (): T): TChannel[T] =

  type Args = tuple[iter: iterator (): T, channel: ptr TChannel[T]]

  # I think I have to wrap the iterator to pass it to createThread
  proc threadFunc(args: Args) {.thread.} =
    echo "Thread is starting"
    let iter = args.iter
    var channel = args.channel[]

    for i in iter():
      echo "Sending ", i
      channel.send(i)

  var thread: TThread[Args]
  var channel: TChannel[T]
  channel.open()

  let args = (f, channel.addr)
  createThread(thread, threadFunc, args)

  result = channel


# example use in some main thread:
iterator test(): int {.closure.} =
  sleep(500)
  yield 1
  sleep(500)
  yield 2

var channel = spawnBackgroundJob[int](test)

for i in 0 .. 10:
  sleep(200)
  echo channel.peek()

echo "Finished"
不幸的是,这没有预期的行为,也就是说,我从未在主线程中收到任何东西。我在IRC上被告知,问题是我不使用全局变量。但是,即使经过很长时间的思考,我也不明白为什么会失败,也不知道是否有办法解决它。问题是,我不能简单地将变量
线程
通道
设置为全局变量,因为它们取决于类型
T
。我还希望避免将其限制为只运行单个anytime算法(或其他固定数字N)。我还被告知,这种方法在总体上没有真正意义,因此我可能只是忽略了这个问题有一个完全不同的解决方案?

原因: 您在发送和接收中使用了两个不同的频道

Nim中的对象分配是深度复制,它们是不同的对象

var channel = args.channel[]

要解释它,请参见下面的代码片段:

type
  A = object
    x: int
    y: int

var a,b: A

var c = cast[ptr A](allocShared0(sizeof(A))) # shared memory allocation

a = c[]
b = c[]

echo a.x, a.y, b.x, b.y, c.x, c.y # output: 000000

a.x = 1
a.y = 2

echo a.x, a.y, b.x, b.y, c.x, c.y # output: 120000

b.x = 3
b.y = 4

echo a.x, a.y, b.x, b.y, c.x, c.y # output: 123400

传递通道输入和输出过程的解决方案: 要传递通道作为参数和返回值,请参阅中Jehan的回答

将Jehan的答案粘贴到这里以供快速参考,并使其在Nim 0.11.2中编译通过

type SharedChannel[T] = ptr TChannel[T]

proc newSharedChannel[T](): SharedChannel[T] =
  result = cast[SharedChannel[T]](allocShared0(sizeof(TChannel[T])))
  open(result[])

proc close[T](ch: var SharedChannel[T]) =
  close(ch[])
  deallocShared(ch)
  ch = nil

proc send[T](ch: SharedChannel[T], content: T) =
  ch[].send(content)


proc recv[T](ch: SharedChannel[T]): T =
  result = ch[].recv

proc someThread(ch: (SharedChannel[string], SharedChannel[bool])) {.thread.} =
  let (mainChannel, responseChannel) = ch
  while true:
    let s = mainChannel.recv
    if s == nil:
      break
    echo s
    responseChannel.send(true)
  responseChannel.send(false)

proc main() =
  var
    mainChannel = newSharedChannel[string]()
    responseChannel = newSharedChannel[bool]()
    th: TThread[(SharedChannel[string], SharedChannel[bool])]
  createThread(th, someThread, (mainChannel, responseChannel))
  for i in 0..2:
    echo("main thread send: " & $i)
    mainChannel.send($i)
    if not responseChannel.recv:
      break
  mainChannel.send(nil)
  joinThread(th)
  close(mainChannel)
  close(responseChannel)

main()
输出:

main thread send: 0
0
main thread send: 1
1
main thread send: 2
2

还有一步,这个问题的解决方案:
我不确定这是否是这里唯一的问题。至少,根据这个答案,我想这应该行得通。我也无法使用Jehan的答案来解决在后台线程中运行迭代器的实际问题。@bluenote10 Jehan的答案适用于Nim 0.11.3,将TThread重命名为thread,将TChannel重命名为Channel。我在这里粘贴了一个Nim 0.11.2版本,它应该可以工作。问题是我无法让它与闭包迭代器一起工作。因为我的anytime算法是闭包,所以我不能简单地将它们放入全局
proc someThread
,如本例所示。@bluenote10是对您的案例的一个介绍。很好,这似乎很好,非常感谢!你介意把这个代码放在你的答案里而不是放在Jehans的例子里吗?这将是更多的主题,并将提供一切,以充分回答这个问题。
main thread send: 0
0
main thread send: 1
1
main thread send: 2
2
import os, threadpool, macros

template spawnBackgroundJob(t: typedesc, chan:ptr TChannel[t], iter: expr): stmt {.immediate.}=
  block:
    proc threadFunc(channel: ptr TChannel[t]) {.thread.} =
      echo "Thread is starting"

      for i in iter:
        echo "Sending ", i
        channel[].send(i)

    channel[].open()

    var thread: TThread[ptr TChannel[t]]
    createThread(thread, threadFunc, chan)
    #joinThread(thread)


# example use in some main thread:
iterator testJob(): int =
  yield 0
  sleep(500)
  yield 1
  sleep(500)
  yield 2

var channel: ptr TChannel[int]
channel = cast[ptr TChannel[int]](allocShared0(sizeof(TChannel[int])))
spawnBackgroundJob(type(int), channel, testJob())

for i in 1 .. 10:
  sleep(200)
  echo channel[].peek()

channel[].close()