我该如何在Perl中实现原子序列?

6
我有以下要求:
  1. 序列对于主机是唯一的(不需要共享增量)
  2. 序列必须单调递增。
  3. 序列在进程间必须持久存在。
  4. 在多个进程同时工作时,增加序列必须是原子性的。
  5. 大部分时间文件将被更新,新值将在更新后读取。但是,也应该可以在没有更新的情况下读取当前值。
我可以拼凑出能够实现这些功能的Perl代码,但我想要一个更优雅的解决方案。

我已经拼凑出了差不多这个样子的 Perl 代码。将当前序列号存储在文件中,并在 flock 包装器内进行访问/更新,算得上优雅吗? - mob
如果我不必亲自编写代码,那当然可以 :) - Chris R
2个回答

5

将序列号存储在文件中,并使用flock确保只有一个进程可以访问它:

sub set {     # seed the sequence number file
    my ($file, $number) = @_;
    open my $fh, '>', $file;
    print $fh $number;
}  # implicit close

sub get {
    my $file = shift;
    my $incr = @_ ? shift : 1;   # get($f) is like get($f,1)
    open my $lock, '>>', "$file.lock";
    flock $lock, 2;
    open my $fh, '<', $file;
    my $seq = <$fh>;
    close $fh;
    set($file, $seq+$incr) if $incr;   # update sequence number
    close $lock;
    return $seq;
}

您可以使用get($file,0)来获取序列号,而不改变它。


这大致符合我的所做的。这个很好。谢谢! - Chris R
为什么需要单独的锁文件?为什么不直接打开文件进行读写('+<'),并对其进行文件锁定? - cjm
1
cjm,请参见http://perl.plover.com/yak/hw-nylug/samples/slide020.html和下一张幻灯片。 - daxim
@daxim,我不确定这是否适用于此。正常情况下是在文件保持锁定的同时读取、增加和写入。truncate不是问题,因为序列是递增的;新数字将始终至少与前一个数字一样长。我认为寻找比关闭和重新打开(以及为锁定文件额外打开)更有效率。 - cjm

0
系统时间提供了一个单调递增的序列,用于解决以下问题(2):
perl -MTime::HiRes=time -lwe "print time"

除非有人重置时钟...

持久性(3)和增量的原子性(4)似乎需要一个锁定数据库。Berkeley DB是一个不错的选择。但是,除非您已经在使用它,否则您可能正在寻找更简单的东西。读取而不更新(5)不会有问题。单调递增序列(2)也不会有问题。

我不确定您所说的“唯一主机”和“共享增量”(1)是什么意思。如果来自不同主机的序列元素具有相同的值,则可以将该方法扩展到所有服务器。否则,您只能拥有一个序列,必须通过网络访问其他人。


不错的尝试,但如果两个进程在同一秒钟内命中相同的库代码,则它们很容易具有相同的值。此外,它是易失性的,因此您读取的值与下一次读取的值可能没有太大关系。 (#5) - Axeman
@Axeman - 系统时间根本不起作用,这一点很清楚。就像我写的那样,有人重置了时钟。如果你能排除这种对时钟的摆弄,就可以通过Berkeley DB来备份时间,从而提供#3、#4和#5。可能也包括#1,但需要澄清。一旦到达那里,您还可以选择选择其他序列,可能只是整数。 - Lumi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接