工作程序MPM下的mod_perl中的进程内协调
我需要在mod_perl中进行一些简单的时区计算。日期时间不是一个选项。通过设置$ ENV {TZ}并使用localtime和POSIX :: mktime可以轻松完成我需要做的事情,但是在线程化MPM下,我需要确保一次仅将一个线程与环境混淆。 (我不关心本地时间的其他用途等)
如何使用互斥锁或者其他锁定策略来序列化(非编组意义上的)对环境的访问?我看过的文档并不能很好地解释我将如何为此目的创建互斥体。也许有些事情我只是不了解我们如何创建互斥体。
更新:是的,我知道需要使用Env :: C来设置TZ。
解决方案
如果我们使用的是Apache 1.3,则无需使用互斥锁。 Apache 1.3产生了许多工作进程,每个工作进程执行一个线程。在这种情况下,我们可以编写:
{ local $ENV{TZ} = whatever_I_need_it_to_be(); # Do calculations here. }
用local
更改变量意味着它会在块的末尾恢复到先前的值,但仍传递到该块内进行的任何子例程调用中。几乎可以肯定,这就是我们想要的。由于每个进程都有自己独立的环境,因此我们不会使用此技术来更改其他进程的环境。
对于Apache 2,我不知道它在fork和线程方面使用什么模型。如果它采用相同的方法来分叉进程,并且每个进程只有一个线程,那么我们就可以了。
如果apache 2使用诚实至善的真实线程,那么那不在我的详细知识范围内,但是我希望另一个可爱的stackoverflow人士可以提供帮助。
祝一切顺利
Paul
(重复我在PerlMonks所说的话...)
BEGIN { my $mutex; sub that { $mutex ||= APR::ThreadMutex->new( $r->pool() ); $mutex->lock(); $ENV{TZ}= ...; ... $mutex->unlock(); } }
但是,当然,除了一次性破解外,lock()应该在控制器中发生,解锁()应该在控制器中发生。
更新:请注意,在子例程中如何初始化$ mutex有一个竞争条件(两个线程几乎可以同时第一次调用that())。我们很可能希望在创建(其他)线程之前初始化$ mutex,但是我不清楚"工人" Apache MPM的详细信息以及如何轻松实现这一点。如果有一些代码"较早"运行,只需从那里调用that()就可以消除竞争。
所有这些都表明APR :: ThreadMutex的接口更加安全:
BEGIN { my $mutex; sub that { my $autoLock= APR::ThreadMutex->autoLock( $mutex ); ... # Mutex automatically released when $autoLock destroyed } }
请注意,autoLock()获取对undef的引用将导致它在初始化$ mutex时使用互斥锁来防止争用。
由于这个问题,mod_perl 2处理%ENV哈希实际上不同于mod_perl1. 在mod_perl 1中,%ENV直接绑定到环境结构,因此更改%ENV会更改环境。在mod_perl 2中,%ENV哈希值是从环境填充的,但更改不会传回。
这意味着我们不再可以使用$ ENV {TZ}来调整时区,尤其是在线程环境中。 Apache2 :: Localtime模块将使其适用于非线程情况(通过使用Env :: C),但是当在线程MPM中运行时,这将是个坏消息。
关于此问题,mod_perl源代码(src / modules / perl / modperl_env.c)中有一些评论:
/* * XXX: what we do here might change: * - make it optional for %ENV to be tied to r->subprocess_env * - make it possible to modify environ * - we could allow modification of environ if mpm isn't threaded * - we could allow modification of environ if variable isn't a CGI * variable (still could cause problems) */ /* * problems we are trying to solve: * - environ is shared between threads * + Perl does not serialize access to environ * + even if it did, CGI variables cannot be shared between threads! * problems we create by trying to solve above problems: * - a forked process will not inherit the current %ENV * - C libraries might rely on environ, e.g. DBD::Oracle */