gpt4 book ai didi

perl - 使用 Perl 并发追加到同一个文件

转载 作者:行者123 更新时间:2023-12-04 00:53:33 26 4
gpt4 key购买 nike

我需要升级用户必须完成 3 个步骤的 Perl CGI 脚本。在他们完成每个步骤后,脚本会记录用户完成的步骤。记录这一点很重要,因此我们可以向用户证明他们只完成了第一步,并没有完成所有三个步骤。

现在,该脚本正在为 CGI 脚本的每个实例创建 1 个日志文件。因此,如果 UserA 执行第 1 步,然后 UserB 执行第 1 步,然后执行第 2 步,然后第 3 步 - 然后 UserA 完成第 2 步和第 3 步,则日志文件的顺序将是。

LogFile.UserA.Step1
LogFile.UserB.Step1
LogFile.UserB.Step2
LogFile.UserB.Step3
LogFile.UserA.Step2
LogFile.UserA.Step3

日志文件以当前时间戳、随机数和进程 PID 命名。

这可以很好地防止同一个文件被多次写入,但目录很快就会获得数千个文件(每个文件只包含几个字节)。有一个过程可以轮换和压缩这些日志,但我不得不这样做,因此脚本每天只记录一个文件,以减少创建的日志文件的数量。

基本上,日志文件将在文件名中包含当前日期,并且无论何时 CGI 脚本需要写入日志,它都会附加到当天的一个日志文件中,而不管用户或他们所处的步骤。

不需要读取日志文件 - 唯一会发生的事情是 CGI 脚本的追加。日志轮换将在 7 天或更早的日志文件上运行。

我的问题是,处理并发追加到此日志文件的最佳方法是什么?我需要在追加之前锁定它吗?我找到了 this page在 Perl Monks 上似乎表明“当多个进程正在写入同一个文件时,并且所有进程都打开了文件以进行附加,数据不应被覆盖。”

我了解到,仅仅因为可以做到并不意味着我应该这样做,但在这种情况下,最安全、最佳实践的方法是什么?

概括:
  • 并发追加到同一个文件
  • 每个追加到文件中只有一行,少于 50 个字符
  • 订单无所谓

  • 谢谢!

    最佳答案

    是的,使用 flock .

    下面是一个示例程序,从典型的前端内容开始:

    #! /usr/bin/perl

    use warnings;
    use strict;

    use Fcntl qw/ :flock /;

    然后我们指定日志的路径和将运行的客户端数量:
    my $log = "/tmp/my.log";
    my $clients = 10;

    要记录消息,请以附加模式打开文件,以便所有写入都自动结束。然后调用 flock等待轮到我们拥有对日志的独占访问权限。起床后,写下消息和 close Handlebars ,它会自动释放锁。
    sub log_step {
    my($msg) = @_;

    open my $fh, ">>", $log or die "$0 [$$]: open: $!";
    flock $fh, LOCK_EX or die "$0 [$$]: flock: $!";
    print $fh "$msg\n" or die "$0 [$$]: write: $!";
    close $fh or warn "$0 [$$]: close: $!";
    }

    现在 fork关闭 $clients子进程以随机间隔完成所有三个步骤:
    my %kids;
    my $id = "A";
    for (1 .. $clients) {
    my $pid = fork;
    die "$0: fork: $!" unless defined $pid;

    if ($pid) {
    ++$kids{$pid};
    print "$0: forked $pid\n";
    }
    else {
    my $user = "User" . $id;
    log_step "$user: Step 1";
    sleep rand 3;
    log_step "$user: Step 2";
    sleep rand 3;
    log_step "$user: Step 3";
    exit 0;
    }

    ++$id;
    }

    不要忘记等待所有 child 退出:
    print "$0: reaping children...\n";
    while (keys %kids) {
    my $pid = waitpid -1, 0;
    last if $pid == -1;

    warn "$0: unexpected kid $pid" unless $kids{$pid};
    delete $kids{$pid};
    }

    warn "$0: still running: ", join(", " => keys %kids), "\n"
    if keys %kids;

    print "$0: done!\n", `cat $log`;

    样本输出:

    [...]
    ./prog.pl:收割 child ...
    ./prog.pl:完成!
    用户A:步骤1
    用户 B:步骤 1
    用户 C:步骤 1
    用户 C:第 2 步
    用户 C:步骤 3
    用户 D:步骤 1
    用户 E:第 1 步
    用户F:步骤1
    用户 G:步骤 1
    用户 H:第 1 步
    用户 I:步骤 1
    用户J:第1步
    用户 D:步骤 2
    用户 D:第 3 步
    用户F:步骤2
    用户 G:第 2 步
    用户 H:第 2 步
    用户 I:步骤 2
    用户 I:步骤 3
    用户 B:步骤 2
    用户A:步骤2
    用户A:步骤3
    用户E:步骤2
    用户 F:第 3 步
    用户 G:步骤 3
    用户J:第2步
    用户J:第3步
    用户 E:第 3 步
    用户 H:步骤 3
    用户 B:步骤 3

    请记住,顺序将因运行而异。

    关于perl - 使用 Perl 并发追加到同一个文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2365642/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com