避免在不同实​​例中多次执行一个函数

Posted

技术标签:

【中文标题】避免在不同实​​例中多次执行一个函数【英文标题】:Avoid multiple executions of a function in different instances 【发布时间】:2022-01-09 22:19:33 【问题描述】:

场景

我已经构建了一个彩票作为 wordpress 插件。用户自行注册,并在第二天第一次调用该网站时,获胜者将被插件切成小块。之后,获胜者将保存在表格中。

要重新开始切块,插件会在表格中查找当天是否有获胜者,如果找到获胜者则应退出。

这个机制确实适用于我的所有测试和几天的生产,但现在由于某种原因,该插件在 3 秒的时间跨度内产生了 15 个获胜者。日志显示没有错误。我已将所有数据复制到我的暂存环境中,但无法重现该行为。

可能发生的事情

我猜服务器负载不足(共享主机),并且有几个 wordpress 实例开始切块。在第一个进程将获胜者写入数据库后,切割停止。

可能的解决方案

我可以将文件写入文件系统以避免对数据库进行耗时的查询,以便其他实例知道正在进行切块并退出该函数。

如果我走在正确的轨道上或者我的想法有误,我会很高兴得到任何建议。

我复制了下面的关键函数。

启动插件

$from = "2021-12-01 00:00:00";
$to = "2021-12-25 23:59:59";
$lottery = new Controller($from, $to);

控制器

class Controller 
    private $_from;
    private $_to;

    public function __construct($from, $to) 
        $this->_from = new \DateTime($from, new \DateTimeZone("Europe/Berlin"));
        $this->_to = new \DateTime($to, new \DateTimeZone("Europe/Berlin"));
        add_action('init', array($this,'init'));
    

    public function init() 
        $day = +date("j");
        $day--;

        $lastDiceDay = Model::getLastDiceDay();
        if ($lastDiceDay >= $day) 
            return;
        

        $dateDay = new \DateTime($this->_from->format('Y-m') . "-$day");
        $mails = Model::getMailsByDay($dateDay);

        $winners = $this->dice($mails, $day);
        Model::saveWinner($winners, $day);
        $this->mail($winners, $day);
    
...

模型函数

class Model 

   public static function getLastDiceDay() 
       global $wpdb;
       $result = $wpdb->get_results('SELECT MAX(day_of_lottery) FROM ' . $wpdb->prefix . 'lottery', ARRAY_A);

       if ($result) 
           return +reset($result[0]);
        else 
        return null;
        
    
    public static function saveWinner($winners, $day) 
        global $wpdb;
        $count = count($winners);
        while ($count--) 
            $winner = $winners[$count]->mail;
            $row = ["winner_mail" => $winner["mail"], "time" => date('Y-m-d H:i:s'), "day_of_lottery" => $day, "email_log_id" =>  $winner["id"]];
            $wpdb->insert( $wpdb->prefix . 'lottery', $row );
        
    
    ...

【问题讨论】:

flock() 或 sem_acquire() 的文档可能会有所帮助。 @Matt Raines:信号量听起来很有希望。非常感谢。 【参考方案1】:

一个简单的解决方法是使用每天运行一次的计划任务(例如通过 cron)并抽出获胜者。

它可能是您公开的 ajax 端点(然后使用服务每天向它发出请求)、Wordpress 任务(需要在 cron 任务中运行 wp_cron 以避免像您一样的竞争条件)或文件在从 cron 任务调用的 WP 根目录中。

这样,您就不必依赖访问您网站的人来触发重要功能。它甚至可以在没有交通的情况下在早上 5 点运行。

【讨论】:

以上是关于避免在不同实​​例中多次执行一个函数的主要内容,如果未能解决你的问题,请参考以下文章

C++并发,同步设计,避免多次执行问题

python函数总结

如何避免多次调用同一个函数?

pytest文档66-工厂化的 fixtures

我有一个相互重叠的 flex 包装 div 容器。这些可能是不同的高度并包裹多次。如何避免重叠?

使用 forEach 时避免回调多次调用