使用 scala actor 时遇到问题

发布于 2024-12-16 10:56:48 字数 645 浏览 2 评论 0原文

我读到,使用 React 时,所有参与者都可以在单个线程中执行。我经常并行处理一个集合并需要输出结果。我不相信 System.out.println 是线程安全的,所以我需要一些保护。我可以通过一种方式(传统方式)做到这一点:就

val lock = new Object
def printer(msg: Any) {
  lock.synchronized {
    println(msg)
  }
}

(1 until 1000).par.foreach { i =>
  printer(i)
}

println("done.")

效率而言,第一个解决方案与使用参与者相比如何?我真的没有创建新线程吗?

val printer = actor {
  loop {
    react {
      case msg => println(msg)
    }
  }
}

(1 until 10000).par.foreach { i =>
  printer ! i
}

println("done.")

然而,这似乎不是一个好的选择,因为参与者代码永远不会完成。如果我将 println 放在底部,它就永远不会被命中,即使看起来它经历了 i 的每次迭代。我做错了什么?

I have read that, when using react, all actors can execute in a single thread. I often process a collection in parallel and need to output the result. I do not believe System.out.println is threadsafe so I need some protection. One way (a traditional way) I could do this:

val lock = new Object
def printer(msg: Any) {
  lock.synchronized {
    println(msg)
  }
}

(1 until 1000).par.foreach { i =>
  printer(i)
}

println("done.")

How does this first solution compare to using actors in terms of efficiency? Is it true that I'm not creating a new thread?

val printer = actor {
  loop {
    react {
      case msg => println(msg)
    }
  }
}

(1 until 10000).par.foreach { i =>
  printer ! i
}

println("done.")

It doesn't seem to be a good alternative however, because the actor code never completes. If I put a println at the bottom it is never hit, even though it looks like it goes through every iteration for i. What am I doing wrong?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

正如您现在使用 Actor 代码一样,只有一个 Actor 执行所有打印操作。正如您从运行代码中看到的,这些值都是由 Actor 按顺序打印出来的,而在并行集合代码中,它们是无序的。我对并行集合不太熟悉,所以不知道两者之间的性能增益。

但是,如果您的代码并行执行大量工作,您可能会希望使用多个参与者。你可以这样做:

def printer = actor {
  loop {
    react {
      case msg => println(msg)
    }
  }
}

val num_workers = 10
val worker_bees = Vector.fill(num_workers)(printer)

(1 until 1000).foreach { i =>
    worker_bees(i % num_workers) ! i
}

def 很重要。这样一来,您实际上是在创建多个演员,而不仅仅是淹没一个演员。

As you have it now with your Actor code, you only have one actor doing all the printing. As you can see from running the code, the values are all printed out sequentially by the Actor whereas in the parallel collection code, they're out of order. I'm not too familiar with parallel collections, so I don't know the performance gains between the two.

However, if your code is doing a lot of work in parallel, you probably would want to go with multiple actors. You could do something like this:

def printer = actor {
  loop {
    react {
      case msg => println(msg)
    }
  }
}

val num_workers = 10
val worker_bees = Vector.fill(num_workers)(printer)

(1 until 1000).foreach { i =>
    worker_bees(i % num_workers) ! i
}

The def is important. This way you're actually creating multiple actors and not just flooding one.

回忆那么伤 2024-12-23 10:56:48

一个 Actor 实例永远不会同时处理多于一条消息。无论为 Actor 分配什么线程池,每个 Actor 实例一次只会占用一个线程,因此可以保证所有打印都将串行处理。

至于未完成,演员的执行永远不会从 reactloop 返回,因此:

val printer = actor {
  loop {
    react {
      case msg => println(msg)
    }
    // This line is never reached because of react
  }
  // This line is never reached because of loop
}

如果您替换 loop使用 while 循环和 receive 进行反应,您将看到 while 循环内的所有内容都按预期执行。

One actor instance will never process more than one message at the time. Whatever thread pool is allocated for the actors, each actor instance will only occupy one thread at the time, so you are guaranteed that all the printing will be processed serially.

As for not finishing, the execution of an actor never returns from a react or a loop, so:

val printer = actor {
  loop {
    react {
      case msg => println(msg)
    }
    // This line is never reached because of react
  }
  // This line is never reached because of loop
}

If you replace loop and react with a while loop and receive, you'll see that everything inside the while loop executes as expected.

來不及說愛妳 2024-12-23 10:56:48

要修复您的 Actor 实现,您需要告诉 Actor 在程序退出之前退出。

val printer = actor {
  loop {
    react {
      case "stop" => exit()
      case msg => println(msg)
    }
  }
}

(1 until 1000).par.foreach { printer ! _ }

printer ! "stop"

在您的两个示例中,都有线程池涉及支持并行库和参与者库,但它们是根据需要创建的。

然而, println 是线程安全的,因为它的内部确实有一个锁。

(1 until 1000).par.foreach { println(_) } // is threadsafe

至于性能,有很多因素。第一个是从多个线程争用的锁转移到仅由一个线程(一个参与者)使用的锁将提高性能。其次,如果您要使用演员并想要表演,请使用
阿卡。与 scala Actor 相比,Akka Actor 的速度快得惊人。另外,我希望 println 正在写入的标准输出将写入文件而不是屏幕,因为涉及显示驱动程序会降低您的性能。

使用并行库对于性能来说非常好,因为这样您就可以利用多个核心进行计算。如果每个计算量非常小,则尝试使用参与者路线进行集中报告。然而,如果每次计算都很重要并且需要相当多的 CPU 时间,那么就坚持只使用 println 本身。你确实没有处于竞争锁的情况。

To fix your actor implementation you need to tell the actor to exit before the program will exit as well.

val printer = actor {
  loop {
    react {
      case "stop" => exit()
      case msg => println(msg)
    }
  }
}

(1 until 1000).par.foreach { printer ! _ }

printer ! "stop"

In both your examples there are thread pools involved backing both the parallels library and the actor library but they are created as needed.

However, println is thread safe as it does indeed have a lock in it's internals.

(1 until 1000).par.foreach { println(_) } // is threadsafe

As for performance, there are many factors. The first is that moving from a lock that multiple threads are contending for to a lock being used by only one thread ( one actor ) will increase performance. Second, if you are going to use actors and want performance, use
Akka. Akka actors are blazingly fast when compared to scala actors. Also, I hope that the stdout that println is writing to is going to a file and not the screen since involving the display drivers is going to kill your performance.

Using the parallels library is wonderful for performance since so you can take advantage of multiple cores for your computation. If each computation is very small then try the actor route for centralized reporting. However if each computation is significant and takes a decent amount of cpu time then stick just using println by itself. You really are not in a contended lock situation.

糖粟与秋泊 2024-12-23 10:56:48

我不确定我能否正确理解你的问题。对我来说,你的演员代码工作正常并终止。

不过,您可以节省地使用 println 进行并行集合,因此您真正需要的是这样的东西:

(1 until 1000).par.foreach { println(_) }

在这里工作就像一个魅力。我假设您已经知道输出顺序会有所不同,但我只想再次强调一下,因为这个问题经常出现。因此,不要指望数字会连续向下滚动屏幕。

I'm not sure I can understand your problem correctly. For me your actor code works fine and terminates.

Nevertheless, you can savely use println for parallel collections, so all you really need is something like this:

(1 until 1000).par.foreach { println(_) }

Works like a charm here. I assume you already know that the output order will vary, but I just want to stress it again, because the question comes up ever so often. So don't expect the numbers to scroll down your screen in a successive fashion.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文