我需要升级用户必须完成 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 上似乎表明“当多个进程正在写入同一个文件时,并且所有进程都打开了文件以进行附加,数据不应被覆盖。”
我了解到,仅仅因为可以做到并不意味着我应该这样做,但在这种情况下,最安全、最佳实践的方法是什么?
概括:
谢谢!
最佳答案
是的,使用 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/