jjzjj

go - 从命名管道连续读取

coder 2023-07-02 原文

我想知道我还有哪些其他选项可以使用 golang 从命名管道连续读取。我当前的代码依赖于在 gorutine 中运行的无限循环;但是帽子使一个 CPU 保持 100% 使用率。

func main() {
....

var wg sync.WaitGroup
fpipe, _ := os.OpenFile(namedPipe, os.O_RDONLY, 0600)
defer fpipe.Close()

f, _ := os.Create("dump.txt")
defer f.Close()
var buff bytes.Buffer

wg.Add(1)
go func() {
        for {
          io.Copy(&buff, fpipe)
          if buff.Len() > 0 {
              buff.WriteTo(f)
           }
         }
    }()

    wg.Wait()
}

最佳答案

介绍

如前所述,如果没有写入者,命名管道读取器将收到 EOF。

但是我发现@JimB 的解决方案不是最优的:

  1. 命名管 Prop 有最大容量(65kB,iirc),它很可能在 100 毫秒的 sleep 周期内被填满。当缓冲区已满时,所有写入程序都会无缘无故地阻塞。
  2. 如果重新启动,您平均会丢失 50 毫秒的数据。同样,没有充分的理由。
  3. 如果您想使用静态缓冲区进行复制,io.CopyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error)恕我直言,这将是更好的解决方案。但这甚至不是必需的,因为 io.Copy (或底层实现)实际上分配了 32kB 的缓冲区。

我的方法

更好的解决方案是等待写入发生并立即将命名管道的内容复制到目标文件。在大多数系统上,有某种关于文件系统事件的通知。包裹github.com/rjeczalik/notify可用于访问我们感兴趣的事件,因为写入事件在大多数重要操作系统上都可以跨平台工作。另一个我们感兴趣的事件是命名管道的删除,因为我们没有任何东西可以读取。

因此,我的解决方案是:

package main

import (
    "flag"
    "io"
    "log"
    "os"

    "github.com/rjeczalik/notify"
)

const (
    MAX_CONCURRENT_WRITERS = 5
)

var (
    pipePath string
    filePath string
)

func init() {
    flag.StringVar(&pipePath, "pipe", "", "/path/to/named_pipe to read from")
    flag.StringVar(&filePath, "file", "out.txt", "/path/to/output file")
    log.SetOutput(os.Stderr)
}

func main() {
    flag.Parse()

    var p, f *os.File
    var err error
    var e notify.EventInfo

    // The usual stuff: checking wether the named pipe exists etc
    if p, err = os.Open(pipePath); os.IsNotExist(err) {
        log.Fatalf("Named pipe '%s' does not exist", pipePath)
    } else if os.IsPermission(err) {
        log.Fatalf("Insufficient permissions to read named pipe '%s': %s", pipePath, err)
    } else if err != nil {
        log.Fatalf("Error while opening named pipe '%s': %s", pipePath, err)
    }
    // Yep, there and readable. Close the file handle on exit
    defer p.Close()

    // Do the same for the output file
    if f, err = os.OpenFile(filePath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600); os.IsNotExist(err) {
        log.Fatalf("File '%s' does not exist", filePath)
    } else if os.IsPermission(err) {
        log.Fatalf("Insufficient permissions to open/create file '%s' for appending: %s", filePath, err)
    } else if err != nil {
        log.Fatalf("Error while opening file '%s' for writing: %err", filePath, err)
    }
    // Again, close the filehandle on exit
    defer f.Close()

    // Here is where it happens. We create a buffered channel for events which might happen
    // on the file. The reason why we make it buffered to the number of expected concurrent writers
    // is that if all writers would (theoretically) write at once or at least pretty close
    // to each other, it might happen that we loose event. This is due to the underlying implementations
    // not because of go.
    c := make(chan notify.EventInfo, MAX_CONCURRENT_WRITERS)

    // Here we tell notify to watch out named pipe for events, Write and Remove events
    // specifically. We watch for remove events, too, as this removes the file handle we
    // read from, making reads impossible
    notify.Watch(pipePath, c, notify.Write|notify.Remove)

    // We start an infinite loop...
    for {
        // ...waiting for an event to be passed.
        e = <-c

        switch e.Event() {

        case notify.Write:
            // If it a a write event, we copy the content of the named pipe to
            // our output file and wait for the next event to happen.
            // Note that this is idempotent: Even if we have huge writes by multiple
            // writers on the named pipe, the first call to Copy will copy the contents.
            // The time to copy that data may well be longer than it takes to generate the events.
            // However, subsequent calls may copy nothing, but that does not do any harm.
            io.Copy(f, p)

        case notify.Remove:
            // Some user or process has removed the named pipe,
            // so we have nothing left to read from.
            // We should inform the user and quit.
            log.Fatalf("Named pipe '%s' was removed. Quitting", pipePath)
        }
    }
}

关于go - 从命名管道连续读取,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45443414/

有关go - 从命名管道连续读取的更多相关文章

  1. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  2. ruby - 如何以所有可能的方式将字符串拆分为长度最多为 3 的连续子字符串? - 2

    我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123

  3. Ruby 写入和读取对象到文件 - 2

    好的,所以我的目标是轻松地将一些数据保存到磁盘以备后用。您如何简单地写入然后读取一个对象?所以如果我有一个简单的类classCattr_accessor:a,:bdefinitialize(a,b)@a,@b=a,bendend所以如果我从中非常快地制作一个objobj=C.new("foo","bar")#justgaveitsomerandomvalues然后我可以把它变成一个kindaidstring=obj.to_s#whichreturns""我终于可以将此字符串打印到文件或其他内容中。我的问题是,我该如何再次将这个id变回一个对象?我知道我可以自己挑选信息并制作一个接受该信

  4. ruby-on-rails - 如何重命名或移动 Rails 的 README_FOR_APP - 2

    当我在我的Rails应用程序根目录中运行rakedoc:app时,API文档是使用/doc/README_FOR_APP作为主页生成的。我想向该文件添加.rdoc扩展名,以便它在GitHub上正确呈现。更好的是,我想将它移动到应用程序根目录(/README.rdoc)。有没有办法通过修改包含的rake/rdoctask任务在我的Rakefile中执行此操作?是否有某个地方可以查找可以修改的主页文件的名称?还是我必须编写一个新的Rake任务?额外的问题:Rails应用程序的两个单独文件/README和/doc/README_FOR_APP背后的逻辑是什么?为什么不只有一个?

  5. ruby - rails 3 redirect_to 将参数传递给命名路由 - 2

    我没有找到太多关于如何执行此操作的信息,尽管有很多关于如何使用像这样的redirect_to将参数传递给重定向的建议:action=>'something',:controller=>'something'在我的应用程序中,我在路由文件中有以下内容match'profile'=>'User#show'我的表演Action是这样的defshow@user=User.find(params[:user])@title=@user.first_nameend重定向发生在同一个用户Controller中,就像这样defregister@title="Registration"@user=Use

  6. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  7. python - 如何读取 MIDI 文件、更改其乐器并将其写回? - 2

    我想解析一个已经存在的.mid文件,改变它的乐器,例如从“acousticgrandpiano”到“violin”,然后将它保存回去或作为另一个.mid文件。根据我在文档中看到的内容,该乐器通过program_change或patch_change指令进行了更改,但我找不到任何在已经存在的MIDI文件中执行此操作的库.他们似乎都只支持从头开始创建的MIDI文件。 最佳答案 MIDIpackage会为您完成此操作,但具体方法取决于midi文件的原始内容。一个MIDI文件由一个或多个音轨组成,每个音轨是十六个channel中任何一个上的

  8. ruby-on-rails - 从应用程序中自定义文件夹内的命名空间自动加载 - 2

    我们目前正在为ROR3.2开发自定义cms引擎。在这个过程中,我们希望成为我们的rails应用程序中的一等公民的几个类类型起源,这意味着它们应该驻留在应用程序的app文件夹下,它是插件。目前我们有以下类型:数据源数据类型查看我在app文件夹下创建了多个目录来保存这些:应用/数据源应用/数据类型应用/View更多类型将随之而来,我有点担心应用程序文件夹被这么多目录污染。因此,我想将它们移动到一个子目录/模块中,该子目录/模块包含cms定义的所有类型。所有类都应位于MyCms命名空间内,目录布局应如下所示:应用程序/my_cms/data_source应用程序/my_cms/data_ty

  9. STM32读取串口传感器数据(颗粒物传感器,主动上传) - 2

    文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,

  10. ruby-on-rails - Rails - 从命名路由中提取 HTTP 动词 - 2

    Rails中有没有一种方法可以提取与路由关联的HTTP动词?例如,给定这样的路线:将“users”匹配到:“users#show”,通过:[:get,:post]我能实现这样的目标吗?users_path.respond_to?(:get)(显然#respond_to不是正确的方法)我最接近的是通过执行以下操作,但它似乎并不令人满意。Rails.application.routes.routes.named_routes["users"].constraints[:request_method]#=>/^GET$/对于上下文,我有一个设置cookie然后执行redirect_to:ba

随机推荐