【问题标题】:Lock Perl sub routine锁定 Perl 子例程
【发布时间】:2023-03-30 15:21:01
【问题描述】:

我写了一个 CGI 脚本。它的子程序之一不能同时执行。即同一用户(或黑客)同时运行两个实例)。我怎样才能避免这种情况?

我使用锁定文件做了以下解决方法,但我不确定它是否安全:

unless (-e $filelock) {
    sub_that_should_be_locked();
}


sub sub_that_should_be_locked {
    open FILE, ">", $filelock;
    flock DATAFILE, LOCK_EX;
    close FILE;

    ...Code that cannot be executed at the same time...   
    ...Code that cannot be executed at the same time...

    unlink $filelock;
}

不应该有等待/队列,并发进程不应该调用 sub_that_should_be_locked

【问题讨论】:

  • 这仍然容易受到竞争条件的影响。如果两个线程同时到达您的unless 行,那么在错误假设它们是唯一正在运行的副本的情况下,它们都会愉快地继续运行。
  • @ikegami 说你希望“完全避免调用sub_that_should_be_locked。对吗?
  • 是的,“sub_that_should_be_locked”不应该同时运行/被调用,因为这是一个获取资金余额并减去所有资金的例程,如果“sub_that_should_be_locked”同时运行,如我所愿,货币余额将变为负数而不是 0。
  • 我们都知道它不应该在多个进程中同时运行。问题是:如果两个进程试图同时运行它会发生什么?第二个进程应该跳过调用sub_that_should_be_locked,还是应该等待第一个进程退出sub_that_should_be_locked
  • 不用等待,第二个进程应该跳过调用sub_that_should_be_locked,因为余额可能不会更新得这么快,所以几分之一秒也可能导致问题

标签: multithreading perl locking flock


【解决方案1】:

您要同步的线程不应创建和删除锁定文件。

你应该确保文件存在在线程启动之前,然后子例程应该使用一个词法文件句柄来打开输入的锁文件.

这样

open my $lock_fh, '<', $lockfile or die $!;
flock $lock_fh, LOCK_EX;

作为它的第一个动作。这将暂停线程,直到它连接到队列中,之后文件将在子例程结束时隐式关闭(并释放它的锁),因为$lock_fh 超出范围

【讨论】:

  • 这不符合 OP 的要求,因为如果其他人持有锁,它会阻塞。 // 另外,期望锁定文件已经存在是一个不必要的限制,它会阻止您将锁定文件放在/tmp
  • @ikegami:我不愿回应,因为您在评论对话中倾向于带领欢乐的舞蹈,但这与 OP 的声明有何不同?
  • 如果有人持有锁,他不想阻止;相反,他想完全避免调用sub_that_should_be_locked。这就是-e 检查的目的,他也明确说明了这一点。
  • @ikegami:我相信你的解决方案应该从描述它正在回答的问题开始
【解决方案2】:

这不安全。由于-eopen 之间存在时间,因此您的代码中存在竞争条件。永远不要将-e 用于锁定文件。

检查文件是否已被锁定,而不是检查文件是否存在。这是使用非阻塞flock 完成的。如果文件没有被锁定,它会返回成功,如果文件已经被锁定,它会返回错误EWOULDBLOCK

请注意,要使其正常工作,您必须在 sub_that_should_be_locked 执行的整个过程中保持锁定。 (你的代码一得到它就释放它。)

use Fcntl qw( LOCK_EX LOCK_NB );

sub get_lock_nb {
   my ($qfn) = @_;

   open(my $fh, '+>:raw', $qfn)
      or die("Unable to open file \"$qfn\": $!\n");

   if (!flock($fh, LOCK_EX | LOCK_NB)) {
      return undef if $!{EWOULDBLOCK};
      die("Unable to lock file \"$qfn\": $!\n");
   }

   return $fh;
}

sub sub_that_should_be_locked {
   ... Mutually exclusive code ...
}

{
   my $lock = get_lock_nb("file.lock");
   sub_that_should_be_locked() if $lock;
}

【讨论】:

  • PS - 请注意,我避免使用全局变量作为文件句柄(open my $fh 而不是open FILE)。尽可能避免使用全局变量。
  • 最终结果如何?所有对 sub_that_should_be_locked 的并发调用都会被拒绝?除了第一个,所有的都会被拒绝?或者是其他东西?我有点困惑,我熟悉 perl 但不熟悉 flock/linux 内部
  • @user3518089,就像你的代码一样,如果一个进程已经在执行它,第二个进程只是避免调用sub_that_should_be_locked。就像你问的那样,它不会等待。如果您有不同的结果,请告诉我(通过编辑您的问题并在此处留下评论说您编辑了您的问题)。
  • @user3518089,将sleep 15; 放入sub_that_should_be_locked,应该很容易看到会发生什么。
  • 我猜你忘了use Fcntl qw( LOCK_EX LOCK_NB );。如果是这样,那也意味着你忘记了use strict; use warnings;(你总是应该这样做的),因为他们会更快地发现这个错误。
猜你喜欢
  • 2013-08-13
  • 1970-01-01
  • 1970-01-01
  • 2011-10-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多