Java和多线程中的命名管道

时间:2013-05-16 16:10:50

标签: java named-pipes

我是否正确我认为在具有2个线程读/写命名管道的同一进程的范围内,根本不会阻止读写器?因此,如果时间错误,可能会遗漏一些数据?

如果有多个进程 - 读者会等到某些数据可用,并且编写器将被阻止,直到读者读取读者提供的所有数据为止?

我打算使用命名管道从外部进程传递几个(几十个,几百个)文件,并在我的Java应用程序中使用它们。编写简单的单元测试以使用一个线程写入管道,另一个 - 用于从管道读取,导致由于缺少数据块而偶发测试失败。

我认为这是因为线程和相同的过程,所以我的测试一般不正确。这个假设是否正确?

以下是一些说明案例的例子:

import java.io.{FileOutputStream, FileInputStream, File}
import java.util.concurrent.Executors
import org.apache.commons.io.IOUtils
import org.junit.runner.RunWith
import org.scalatest.FlatSpec
import org.scalatest.junit.JUnitRunner

@RunWith(classOf[JUnitRunner])
class PipeTest extends FlatSpec {

  def md5sum(data: Array[Byte]) = {
    import java.security.MessageDigest
    MessageDigest.getInstance("MD5").digest(data).map("%02x".format(_)).mkString
  }

  "Pipe" should "block here" in {
    val pipe = new File("/tmp/mypipe")
    val srcData = new File("/tmp/random.10m")
    val md5 = "8e0a24d1d47264919f9d47f5223c913e"
    val executor = Executors.newSingleThreadExecutor()
    executor.execute(new Runnable {
      def run() {
        (1 to 10).foreach {
          id =>
            val fis = new FileInputStream(pipe)
            assert(md5 === md5sum(IOUtils.toByteArray(fis)))
            fis.close()
        }
      }
    })
    (1 to 10).foreach {
      id =>
        val is = new FileInputStream(srcData)
        val os = new FileOutputStream(pipe)
        IOUtils.copyLarge(is, os)
        os.flush()
        os.close()
        is.close()
        Thread.sleep(200)
    }
  }

}

没有 Thread.sleep(200)测试因原因无法通过

  • 破管异常
  • 错误的MD5总和

设置此延迟 - 它的效果非常好。我正在使用带有10兆字节随机数据的文件。

2 个答案:

答案 0 :(得分:5)

这是您代码中非常简单的竞争条件:您正在将固定大小的消息写入管道,并假设您可以读取相同的消息。但是,您不知道任何给定读取的管道中有多少数据可用。

如果您在写入前写上写入的字节数,并确保每个只读取该字节数,您将看到管道的工作方式与广告完全相同。

如果你有多个编写器和/或多个读者的情况,我建议使用实际的消息队列。实际上,我建议在任何情况下使用消息队列,因为它解决了消息边界划分的问题;重新发明那个特定的轮子没什么意义。

答案 1 :(得分:1)

  

我是否正确我认为在同一进程的范围内有2个线程读/写命名管道根本不会阻止读写器?

除非您使用的是非阻塞I / O,否则不会这样做。

  

因此,如果时间错误,可能会遗漏一些数据?

除非您使用的是非阻塞I / O,否则不会这样做。