尝试重新定义IO :: Tee :: PRINT以确保线程安全

时间:2014-04-24 17:04:26

标签: multithreading perl logging flock

Thanks to some help,我已经开始尝试为我的脚本启用线程安全日志记录 - 但是,我似乎没有让它正常工作:

use Fcntl ':flock';

no warnings 'redefine';
sub IO::Tee::PRINT
{
    my $self = shift;
    my $ret = 1;
    foreach my $fh (@$self) {
        flock($fh, LOCK_EX);
        print "\n\t\ttestA\n";  #<-- added for testing
        undef $ret unless print $fh @_;
        flock($fh, LOCK_UN);
        print "\t\ttestB\n";    #<-- added for testing
    }
    return $ret;
}

my $Info_tee = IO::Tee->new(\*STDOUT, ">$ENV{DOM}\\build.log");

当我到达脚本的线程部分时:

print $Info_tee "\n------------------------------------------------------\n";
print $Info_tee "\n\t\t*** Performing Action: \'$cmd\' on $comp ***";

我输出到STDOUT(4个主题)是:

                testA
                testA

------------------------------------------------------
                testB

                testA

------------------------------------------------------
                testB

------------------------------------------------------
                testB

                testA

                testA

------------------------------------------------------
                testB

..然后脚本锁定。我做错了什么?

修改:我已经创建了一个简单的问题示例here - 我注意到如果您从脚本中remove the queue,一切似乎都可以正常工作设计的。

3 个答案:

答案 0 :(得分:1)

简单方法

只需在共享资源周围使用explicit mutex即可。这清晰,简洁,容易:

use threads;
use threads::shared;

my $mutex : shared; # will synchronize access to our shared IO::Tee resource

async {
  ...
  { lock $mutex; print $shared_resource "I did something"; }
  ...
}

注意,您的同步需要是互斥锁或其他一些线程构造。 flock 和朋友被设计用于进程间锁定,generally don't have desirable multithreaded semantics

更难的方式

您似乎想要的替代方法,即重新定义或扩展IO :: Tee以支持隐式互斥锁,是不明显的,冗长的,更具挑战性的。

为什么呢? IO :: Tee对象带有状态,不适合多线程使用。 (也就是说,简单地同步PRINT / PRINTF调用可能是不够的。)每个对象实际上是两个对象tie()d在一起,一个是GLOB,一个是在该glob中插入的ARRAY。 Glob不能在perl线程下共享,并且在共享时清除arrayrefs。当然,它并不棘手,但是只需使用显式互斥锁来控制对共享资源的线程访问,工作量似乎就不成比例了。

答案 1 :(得分:0)

你一如既往地不清楚。


如果您遇到的问题是脚本锁定,则不是您发布的任何内容。


如果您发布的问题是在释放锁之后正在打印东西,那是因为您需要在释放锁之前刷新缓冲区。

STDOUT->flush();
$fh->flush();

如果问题是你得到了以下内容,那是因为你没有足够长时间保持锁定(正如你之前被告知的那样)。

------------------------------------------------------
------------------------------------------------------
*** Performing Action: 'action' on comp1 ***
*** Performing Action: 'action' on comp2 ***

答案 2 :(得分:0)

你可以避免重新发明轮子。有许多日志模块支持这种开箱即用的功能。

一个例子是Log4perl:perl: Will Log::Log4perl work in a multi-threaded environment?

http://search.cpan.org/dist/Log-Log4perl/lib/Log/Log4perl/FAQ.pm#How_can_I_run_Log%3a%3aLog4perl_under_mod_perl

此致