【问题标题】:Intra-process coordination in mod_perl under the worker MPMworker MPM 下 mod_perl 中的进程内协调
【发布时间】:2010-09-12 13:31:32
【问题描述】:

我需要在 mod_perl 中进行一些简单的时区计算。 DateTime 不是一个选项。我需要做的很容易通过设置 $ENV{TZ} 并使用 localtime 和 POSIX::mktime 来完成,但是在线程 MPM 下,我需要确保一次只有一个线程与环境混为一谈。 (我不关心本地时间的其他用途等)

如何使用互斥锁或其他锁定策略来序列化(在非编组意义上)对环境的访问?我看过的docs 并没有很好地解释我将如何为这个用途创建一个互斥锁。也许有一些关于你如何创建互斥锁的事情我只是不明白。

更新:是的,我知道需要使用 Env::C 来设置 TZ。

【问题讨论】:

    标签: perl multithreading apache2 mutex


    【解决方案1】:

    如果您使用的是 apache 1.3,那么您不需要使用互斥锁。 Apache 1.3 产生了许多工作进程,每个工作进程执行一个线程。在这种情况下,你可以写:

    {
        local $ENV{TZ} = whatever_I_need_it_to_be();
    
        # Do calculations here.
    }
    

    local 更改变量意味着它恢复到块末尾的前一个值,但仍会传递到从该块内进行的任何子例程调用。这几乎肯定是你想要的。由于每个进程都有自己独立的环境,因此您不会使用此技术更改其他进程的环境。

    对于 apache 2,我不知道它在分叉和线程方面使用什么模型。如果它保持相同的方法来分叉进程并每个都有一个线程,那么你很好。

    如果 apache 2 使用诚实至善的真实线程,那超出了我的详细知识范围,但我希望另一个可爱的 stackoverflow 人可以提供帮助。

    一切顺利,

    Paul
    

    【讨论】:

    • Apache 2 提供了多个称为 MPM 的分叉/线程模型。我正在使用worker MPM,它使用多个进程和每个进程的多个线程。类似于 Apache 1 的 MPM 称为 prefork。
    【解决方案2】:

    (重复我在 PerlMonks 上说过的话......)

    BEGIN {
        my $mutex;
    
        sub that {
            $mutex ||= APR::ThreadMutex->new( $r->pool() );
            $mutex->lock();
    
            $ENV{TZ}= ...;
            ...
    
            $mutex->unlock();
        }
    }
    

    当然,lock() 应该发生在 c'tor 中,而 unlock() 应该发生在 d'tor 中,除了一次性的 hack。

    更新:请注意,在子例程中如何初始化 $mutex 存在竞争条件(两个线程几乎同时第一次调用 that())。您很可能希望在创建(附加)线程之前初始化 $mutex,但我不清楚“worker”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 中设置 $ENV{TZ} 在 Apache 2 中不起作用。这些更改不会反映回 C 环境,这意味着它们不会影响本地时间等内容的输出。
    • 谢谢。这就是为什么我投票赞成你的答案:) 一些讨论/链接关于为什么 Apache 只是选择“踢”这个可能很有趣。
    • 从 mod_perl 源中找到了一些有趣的 cmets,对它有所了解。
    【解决方案3】:

    由于这个问题,mod_perl 2 实际上处理 %ENV 哈希的方式与 mod_perl 1 不同。在 mod_perl 1 中,%ENV 直接绑定到 environ 结构,因此更改 %ENV 会改变环境。在 mod_perl 2 中,%ENV 哈希是从 environ 填充的,但不会传回更改。

    这意味着您不能再使用 $ENV{TZ} 来调整时区——尤其是在线程环境中。 Apache2::Localtime 模块将使其适用于非线程情况(通过使用 Env::C),但在线程 MPM 中运行时,这将是个坏消息。

    mod_perl 源 (src/modules/perl/modperl_env.c) 中有一些关于此问题的 cmets:

    /* * 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
     */
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-01-11
      • 1970-01-01
      • 2012-12-02
      • 2013-04-08
      • 2013-06-21
      • 2018-02-25
      • 2016-06-20
      • 2018-11-17
      相关资源
      最近更新 更多