如何进行原子交换 - Scala方式?

gre*_*man 5 multithreading scala atomic

问题

我有这样的代码

var ls = src.iter.toList
src.iter = ls.iterator
Run Code Online (Sandbox Code Playgroud)

(这是我的迭代器包装器的复制构造函数的一部分),它读取源迭代器,并在下一行中将其设置回来.问题是,这两行必须是原子的(特别是如果你认为我改变了复制构造函数的来源 - 我不喜欢它,但很好......).

我读过有关Actors但我看不到它们是如何适合的 - 它们看起来更像是异步执行的机制.我已经阅读了Java解决方案并在Scala中使用它们,例如:http://naedyr.blogspot.com/2011/03/atomic-scala.html

我的问题是:什么是使Scala成为原子操作最多的Scala方法?我不想为此使用一些重型火炮,而且我也不想使用一些外部资源.换句话说 - 看起来和感觉"正确"的东西.

我喜欢上面链接中提供的解决方案,因为这正是我所做的 - 交换引用.如果我理解正确,我只会保护那两行,而其他代码不需要改变!但我会等待明确的答案.

背景

因为每个第N个问题,而不是回答我读"但为什么你使用...",这里: 如何在Scala中复制迭代器?:-)

我需要复制iterator(制作一个fork),这样的解决方案是我读到的最"正确".问题是,它破坏了原始的迭代器.

解决方案

例如:http: //www.ibm.com/developerworks/java/library/j-scala02049/index.html

我在这里看到的唯一问题是,我必须锁定这两行以及iter上的所有其他用法.现在这是次要的,但是当我添加一些代码时,很容易忘记添加额外的锁.

我不是说"不",但我没有经验,所以我想从熟悉Scala的人那里得到答案,指出一个方向 - 哪种解决方案最适合这样的任务,并且从长远来看.

不可变的迭代器

虽然我很欣赏Paradigmatic的解释,但我不知道这种方法如何适合我的问题.问题是IteratorWrapper类必须包装迭代器 - 即原始迭代器应隐藏在类中(通常通过将其设为私有来完成).hasNext()和next()等方法也应该包装好.通常next()会改变对象的状态(迭代器),所以在不可变的IteratorWrapper的情况下,它应该返回新的IteratorWrapper和next()的状态(成功与否).如果raw next()失败,另一个解决方案是返回NULL,无论如何,这使得使用这样的IteratorWrapper不是很方便.

更糟糕的是,仍然没有简单的方法来复制这样的IteratorWrapper.

所以要么我想念一些东西,要么实际上经典的方法使得一段代码原子化更清晰.因为所有负担都包含在类中,并且用户不必为IteratorWrapper处理数据的方式付出代价(在这种情况下是原始迭代器).

par*_*tic 4

Scala 方法是尽可能支持不变性(而且通常是可能的)。那么你就不再需要复制构造函数、锁、互斥锁等。

例如,您可以将迭代器转换为Listat 对象构造。由于列表是不可变的,因此您可以安全地共享它们而无需锁定:

class IteratorWrapper[A]( iter: Iterator[A] ) {
  val list = iter.toList

  def iteratorCopy = list.iterator
}
Run Code Online (Sandbox Code Playgroud)

在这里,IteratorWrapper也是不可变的。您可以安全地传递它。但如果您确实需要更改包装的迭代器,则需要更苛刻的方法。例如你可以:

  1. 使用锁
  2. 将包装器转换为Actor
  3. 使用 STM(akka 或其他实现)。

澄清:我缺乏有关您的问题限制的信息。但这是我的理解。

多个线程必须同时遍历一个Iterator. 一种可能的方法是在将引用传递给线程之前复制它。然而,Scala 实践的目标是共享不需要复制的不可变对象。

使用复制策略,您可以编写如下内容:

//A single iterator producer
class Producer {
  val iterator: Iterator[Foo] = produceIterator(...)
}

//Several consumers, living on different threads
class Consumer( p: Producer ) {
  def consumeIterator = {
    val iteratorCopy = copy( p.iterator ) //BROKEN !!!
    while( iteratorCopy.hasNext ) {
      doSomething( iteratorCopy.next )
    } 
  }  
}
Run Code Online (Sandbox Code Playgroud)

然而,实现线程安全的复制方法很困难(或很慢)。使用不变性的可能解决方案是:

class Producer {
  val lst: List[Foo] = produceIterator(...).toList 
  def iteratorCopy = list.iterator
}

class Consumer( p: Producer ) {
  def consumeIterator = {
    val iteratorCopy = p.iteratorCopy 
    while( iteratorCopy.hasNext ) {
      doSomething( iteratorCopy.next )
    } 
  }  
}
Run Code Online (Sandbox Code Playgroud)

生产者将produceIterator在施工时致电一次。它是不可变的,因为它的状态只是一个也不可变的列表。它iteratorCopy也是线程安全的,因为在创建副本时不会修改列表(因此多个线程可以同时遍历它而无需锁定)。

请注意,调用list.iterator不会遍历列表。因此它不会以任何方式降低性能(而不是每次都真正复制迭代器)。