Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/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
Multithreading 如何进行原子交换——Scala方式? 问题_Multithreading_Scala_Atomic - Fatal编程技术网

Multithreading 如何进行原子交换——Scala方式? 问题

Multithreading 如何进行原子交换——Scala方式? 问题,multithreading,scala,atomic,Multithreading,Scala,Atomic,我有这样的密码 var ls = src.iter.toList src.iter = ls.iterator (这是我的迭代器包装的复制构造函数的一部分),它读取源迭代器,并在下一行将其设置回原处。问题是,这两条线必须是原子的(特别是如果你认为我改变了复制构造函数的来源——我不喜欢它,但是很好……)。 我已经读过关于actor的文章,但我不知道它们如何适合这里——它们看起来更像是异步执行的机制。我已经阅读了Java解决方案,并在Scala中使用它们,例如: 我的问题是:什么是使某些操作原子化

我有这样的密码

var ls = src.iter.toList
src.iter = ls.iterator
(这是我的迭代器包装的复制构造函数的一部分),它读取源迭代器,并在下一行将其设置回原处。问题是,这两条线必须是原子的(特别是如果你认为我改变了复制构造函数的来源——我不喜欢它,但是很好……)。 我已经读过关于actor的文章,但我不知道它们如何适合这里——它们看起来更像是异步执行的机制。我已经阅读了Java解决方案,并在Scala中使用它们,例如:

我的问题是:什么是使某些操作原子化的最有效的Scala方法?我不想为此使用重炮,也不想使用外部资源。换句话说——看起来和感觉“正确”的东西

我有点喜欢上面链接中提供的解决方案,因为这正是我要做的——交换引用。如果我理解正确,我只会保护这两行代码,其他代码不需要修改!但我会等待最终的答案

背景 因为每个第n个问题,我都会读到“但是你为什么使用…”而不是答案,这里: :-)

我需要复制迭代器(生成一个fork),这样的解决方案是我读到的最“正确”的。问题是,它破坏了原始迭代器

解决 锁定

例如:

我在这里看到的唯一问题是,我必须锁定这两条线路,以及iter上的每一次其他使用。现在这是一件小事,但当我添加一些代码时,很容易忘记添加额外的锁

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

不可变迭代器

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

更糟糕的是,仍然没有简单的方法来复制这样的迭代器包装器


所以,要么我错过了一些东西,要么事实上,让代码原子化的经典方法更干净。因为所有的负担都包含在类中,用户不必为IteratorWrapper处理数据的方式(本例中为原始迭代器)付出代价。

Scala方法是在可能的情况下(通常是可能的)支持不变性。这样,您就不再需要复制构造函数、锁、互斥体等了

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

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

  def iteratorCopy = list.iterator
}
这里,
IteratorWrapper
也是不可变的。你可以安全地把它传过去。但是,如果您真的需要更改包装的迭代器,您将需要更严格的方法。例如,您可以:

  • 使用锁
  • 将包装器转换为
    Actor
  • 使用STM(akka或其他实现)

  • 澄清:我缺乏关于您的问题约束的信息。但我是这样理解的

    多个线程必须同时遍历
    迭代器
    。一种可能的方法是在将引用传递给线程之前复制它。然而,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 )
        } 
      }  
    }
    
    然而,实现线程安全的复制方法是困难的(或缓慢的)。使用不变性的可能解决方案是:

    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 )
        } 
      }  
    }
    
    生成器将在构造时调用一次
    produceIterator
    。它是不可变的,因为它的状态只是一个同样不可变的列表。
    iteratorCopy
    也是线程安全的,因为创建副本时不会修改列表(因此多个线程可以同时遍历它而不必锁定)


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

    你为什么不用锁呢?那将是原子锁。你是说类似于C#lock的锁?(我问这个问题是因为我刚刚从Scala开始)--好吧,这就是我问的原因:-)但是如果这类似于C#,这意味着我必须在使用“iter”成员的每一块中添加锁。一旦我忘记了,我就会有问题。我不是说“不”,我只是在大声思考。你为什么不限制对
    src.iter
    的直接访问,并且只使用进行锁定的方法访问它?你是说私有的?当然,它是类的私有成员,但在类中它可以更改(例如,通过另一个复制构造函数)。关于锁定——这是解决此问题的最佳解决方案吗(例如与比较和交换相比)?如果是的话——好的。Akka中始终支持STM,这应该会提供您正在寻找的原子特性。也许我还没有赶上Scala——看看您的第一行,为“列表”赋值。它不是重复了“国际热核实验堆”吗?这意味着您创建了IteratorWrapper实例,好的,但是从POV外部您忽略了整个集合。你没有复制,你把内容从一个地方“转移”到另一个地方。必须遍历迭代器一次才能构建列表。但是当实例化
    IteratorWrapper
    时,这种情况只会发生一次。构造完成后,可以将其丢弃。2.advantag