首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >基于 Redis 消息队列实现 Laravel 事件监听及底层源码探究

基于 Redis 消息队列实现 Laravel 事件监听及底层源码探究

作者头像
学院君
发布于 2021-01-08 07:48:26
发布于 2021-01-08 07:48:26
3.8K00
代码可运行
举报
文章被收录于专栏:学院君的专栏学院君的专栏
运行总次数:0
代码可运行

在 Laravel 中,除了使用 dispatch 辅助函数通过 Illuminate\Bus\Dispatcher 显式推送队列任务外,还可以通过事件监听的方式隐式进行队列任务推送,在这个场景下,事件监听器实际上扮演了「任务类」的角色。

还是以文章浏览数更新为例。开始之前,我们先来给大家演示下事件监听和处理的基本实现。

事件监听基本使用

首先创建一个文章浏览事件类 PostViewed

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
sail artisan make:event PostViewed

然后编写这个事件类代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?php

namespace App\Events;

use App\Models\Post;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class PostViewed
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public Post $post;

    /**
     * Create a new event instance.
     *
     * @param Post $post
     */
    public function __construct(Post $post)
    {
        $this->post = $post;
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return Channel|array
     */
    public function broadcastOn()
    {
        return new PrivateChannel('channel-name');
    }
}

事件类的作用就是装载事件相关的数据,这里我们引入了 Post 模型实例,以便在事件监听器中进行相应的处理,事件类中默认还有一个 broadcastOn 表示事件的广播通道,我们在后面介绍广播时再详细介绍这个方法。

有了事件之后,还要创建一个监听这个事件的处理器:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
php artisan make:listener IncreasePostViews

编写处理器代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?php

namespace App\Listeners;

use App\Events\PostViewed;
use Illuminate\Support\Facades\Redis;

class IncreasePostViews
{
    /**
     * Create the event listener.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Handle the event.
     *
     * @param PostViewed $event
     * @return void
     */
    public function handle(PostViewed $event)
    {
        if ($event->post->increment('views')) {
            Redis::zincrby('popular_posts', 1, $event->post->id);
        }
    }
}

我们将之前队列任务类的 handle 方法代码搬到了事件监听器的 handle 方法中,作为文章浏览事件发生时的处理逻辑。

要建立事件与监听器之间的映射关系,保证事件发生时可以通过监听器对其进行处理,需要在 EventServiceProvider 中维护一个监听数组配置:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
protected $listen = [
    ...
    PostViewed::class => [
        IncreasePostViews::class
    ],
];

以事件做键,事件监听器做值,表示一个事件可以同时被多个事件监听器监听和处理。

Laravel 还提供了事件自动发现功能,不过考虑到反射性能较差,我们这里还是使用传统的手动注册方式。

这样一来,当我们在 PostControllershow 方法中触发 PostViewed 事件时:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 浏览文章
public function show($id)
{
    $post = $this->postRepo->getById($id);
    // 触发文章浏览事件
    event(new PostViewed($post));
    return "Show Post #{$post->id}, Views: {$post->views}";
}

就会触发监听该事件的所有处理器类执行 handle 方法处理这个事件,默认情况下,事件监听器是同步执行的,所以你可以立即看到文章浏览数被更新:

基于队列处理事件监听

这只是一个更新单条数据库记录的事件处理,如果是耗时操作,比如网络请求、邮件发送、大的数据库事务等,同步处理事件监听会导致这个页面浏览要加载很长时间,降低用户体验和系统负载,所以 Laravel 还支持将事件处理推送到消息队列异步处理,提升系统性能,优化用户体验。

要让事件处理自动推送到消息队列,只需要让对应的事件监听器类和队列任务类一样实现 ShouldQueue 接口即可,为了方便与队列系统交互,你还可以使用 InteractsWithQueue Trait(这一步不是必须的):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
...
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;

class IncreasePostViews implements ShouldQueue
{
    use InteractsWithQueue;

    ...
}

如果你在创建之初就已经明确知道这个事件监听器的处理操作会推送到队列,可以在创建事件监听器的时候使用 --queued 选项:php artisan make:listener IncreasePostViews

其他代码不用做任何调整,这样,当事件触发时,对于这个实现了 ShouldQueue 接口的监听器,Laravel 会自动将其作为「任务类」推送到消息队列(默认连接、默认队列名称),如果你想要自定义队列连接、队列名称、延迟和重试之类的配置,可以像在队列任务类中一样在这个监听器类中定义 connectionqueuedelaytries 等属性:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public string $queue = 'events';

这个时候,当你刷新浏览器中的文章浏览页面时,就会发现不再执行文章浏览数更新操作了,说明这个处理操作被推送到队列系统了:

你可以在 Redis 队列 laravel_database_queues:events 中看到对应的消息数据:

这个消息数据对应的 JSON 数据如下:

其中的 data.command 反序列化后的结果如下:

其实就是 IncreasePostViews 监听器类,可以看到这些数据结构和消息队列一模一样,所以可以大胆猜测它们底层共用了同一套代码。

为了让 events 队列中的事件监听器被处理掉,运行如下命令启动消息队列处理进程:

你可以到数据库中验证 posts.id = 88 的记录,如果 views 字段值等于 97,则表明文章浏览事件被成功处理。

底层实现源码

为了一探事件监听和处理的底层实现原理,我们到 Laravel 底层查看相关的源码实现。

注册事件与对应的监听器处理逻辑

在 Laravel 应用启动过程中,会调用 App\Providers\EventServiceProviderregister 方法基于 listen 数组注册事件和监听器的映射关系:

这里的 Event 门面是在 Illuminate\Events\EventServiceProviderregister 方法中注册的 events 服务的代理:

Event::listen 调用的就是 Dispatcher 类的 listen 方法,需要注意的是这里的 Dispatcher 对应着 Illuminate\Events\Dispatcher 类,而不是队列任务分发时调用的 Illuminate\Bus\Dispatcher 类。这两个类不是同一个类,也分别实现了不同接口。

在初始化 Illuminate\Events\Dispatcher 实例时还通过 setQueueResolver 方法基于闭包函数设置了队列服务实例,如果事件处理要推送到队列,则使用这个服务实例进行操作,该闭包函数返回的服务实例正是 QueueManager 对象实例。

Illuminate\Events\Dispatcherlisten 方法中,我们得以窥见事件及对应监听器处理逻辑的注册源码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public function listen($events, $listener = null)
{
    if ($events instanceof Closure) {
        return $this->listen($this->firstClosureParameterType($events), $events);
    } elseif ($events instanceof QueuedClosure) {
        return $this->listen($this->firstClosureParameterType($events->closure), $events->resolve());
    } elseif ($listener instanceof QueuedClosure) {
        $listener = $listener->resolve();
    }

    foreach ((array) $events as $event) {
        if (Str::contains($event, '*')) {
            $this->setupWildcardListen($event, $listener);
        } else {
            $this->listeners[$event][] = $this->makeListener($listener);
        }
    }
}

不论是基于闭包的,还是基于通配符的,还是基于 PHP 类的(这些示例都可以在 Laravel 事件文档中看到),在这里通通一览无余,以我们定义的 $listen 数组为例,最终所有事件类和对应监听器处理逻辑映射关系都被维护到 Illuminate\Events\Dispatcherlisteners 数组中,Dispatcher 是以单例模式绑定到服务容器的,所以 listeners 数组在启动期间一经注册完毕,在当前请求生命周期全局有效。

所有事件对应的监听器处理逻辑此时都是闭包函数,只有在对应事件被触发时才会真正执行,我们在执行时再详细剖析 makeListener 方法的底层实现。

事件触发时底层处理逻辑

event 辅助函数对应的实现代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function event(...$args)
{
    return app('events')->dispatch(...$args);
}

这里的 app('events') 会被解析为上面的 Illuminate\Events\Dispatcher 对象实例,所以当我们通过 event 函数触发事件时,实际上调用的是 Illuminate\Events\Dispatcher 类的 dispatch 方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public function dispatch($event, $payload = [], $halt = false)
{
    [$event, $payload] = $this->parseEventAndPayload(
        $event, $payload
    );

    if ($this->shouldBroadcast($payload)) {
        $this->broadcastEvent($payload[0]);
    }

    $responses = [];

    foreach ($this->getListeners($event) as $listener) {
        $response = $listener($event, $payload);

        if ($halt && ! is_null($response)) {
            return $response;
        }

        if ($response === false) {
            break;
        }

        $responses[] = $response;
    }

    return $halt ? null : $responses;
}

在这个方法中,我们首先从参数中解析出事件名和载荷数据。

载荷数据在广播时会用到,我们后面介绍广播时再详细探讨它,这里先忽略。

如果这是个广播事件,则进行广播事件推送处理,然后继续往后执行,从 listeners 数组中通过事件名解析出所有与之映射的监听器处理逻辑,由于映射的监听器处理逻辑此时都是闭包函数,所以需要调用对应的闭包函数才能真正执行这些处理逻辑:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$response = $listener($event, $payload);
不同类型监听器底层处理逻辑

我们接下来来分析 makeListener 方法底层是如何通过闭包函数封装监听器的事件处理逻辑的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public function makeListener($listener, $wildcard = false)
{
    if (is_string($listener)) {
        return $this->createClassListener($listener, $wildcard);
    }

    if (is_array($listener) && isset($listener[0]) && is_string($listener[0])) {
        return $this->createClassListener($listener, $wildcard);
    }

    return function ($event, $payload) use ($listener, $wildcard) {
        if ($wildcard) {
            return $listener($event, $payload);
        }

        return $listener(...array_values($payload));
    };
}

对于字符串类型的监听器类,它会调用 Dispatcher 类的 createClassListener 方法创建监听器类实例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public function createClassListener($listener, $wildcard = false)
{
    return function ($event, $payload) use ($listener, $wildcard) {
        if ($wildcard) {
            return call_user_func($this->createClassCallable($listener), $event, $payload);
        }

        $callable = $this->createClassCallable($listener);

        return $callable(...array_values($payload));
    };
}

该方法又会调用 createClassCallable 方法对监听器类做进一步处理:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
protected function createClassCallable($listener)
{
    [$class, $method] = is_array($listener)
                        ? $listener
                        : $this->parseClassCallable($listener);

    if (! method_exists($class, $method)) {
        $method = '__invoke';
    }

    if ($this->handlerShouldBeQueued($class)) {
        return $this->createQueuedHandlerCallable($class, $method);
    }

    return [$this->container->make($class), $method];
}

在这个方法中,首先会解析监听器处理事件的方法,默认是 handle 方法,如果该方法不存在,则使用 __invoke 方法(所以在 IncreasePostViews 中,还可以定义 __invoke 方法替代 handle),如果监听器类实现了 ShouldQueue 接口,则调用 createQueuedHandlerCallable 方法定义将其推送到队列的闭包函数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
protected function createQueuedHandlerCallable($class, $method)
{
    return function () use ($class, $method) {
        $arguments = array_map(function ($a) {
            return is_object($a) ? clone $a : $a;
        }, func_get_args());

        if ($this->handlerWantsToBeQueued($class, $arguments)) {
            $this->queueHandler($class, $method, $arguments);
        }
    };
}

这里还有一个判断逻辑,handlerWantsToBeQueued 会基于监听器类定义的 shouldQueue 方法判断当前事件监听器是否满足推送到队列执行的条件(所以可以在事件监听器类中基于这个方法实现按条件推送到队列),如果不满足也不会推送到队列,如果满足则调用 queueHandler 方法将当前事件监听器作为任务类推送到队列:

这里的队列服务实例正是从 EventServiceProvider 注册 events 服务时通过 setQueueResolver 设置的队列服务中解析出来的,最终对应的是 QueueManager 对象实例,这里可以基于事件监听器定义的 connectionqueuedelay 属性解析队列连接、名称和延迟推送时间,如果监听器类没有定义,则使用默认值,后面的实现源码想必我也不用贴出来了,参考前面消息队列底层源码即可(当前是基于 Redis 驱动的队列系统,对应的队列实现类是 RedisQueue)。

回到 createClassCallable 方法,如果当前监听器类没有实现 ShouldQueue 接口,则直接以数组形式返回当前监听器类对象实例和处理事件的方法,以 IncreasePostViews 为例,是 handle 方法。而在上一层 createClassListener 方法中,不管推送到队列还是直接执行,所有事件监听器处理逻辑最终都会通过闭包函数封装返回给上一层调用代码。

回到最上层 makeListener 方法,如果是通配符事件或者基于闭包含函数定义的监听器则在前面处理的基础上再包裹一层闭包函数返回。

至此,我们就取得了所有类型事件监听器的处理逻辑闭包函数:

  • 对于字符串类型的监听器类,如果实现了 ShouldQueue 接口,则返回推送到队列的闭包函数,否则返回直接执行监听器实例处理方法的闭包函数;
  • 对于通配符事件监听器和基于闭包的事件监听器,则在之前处理基础上在外层再包裹一层闭包函数返回。

这样,当我们在 Illuminate\Events\Dispatcher 类的 dispatch 方法中调用如下这行代码时:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$response = $listener($event, $payload);

listenerevent 和

小结

好了,到这里,你应该对 Laravel 事件监听和处理的全貌了然于胸了吧,事件的监听处理和队列推送消费很像,都是把生产者和消费者隔离,从而降低业务代码的耦合,提高系统的水平扩展性,而且事件处理部分也可以推送到队列处理,进而提升系统性能,这个时候,事件监听和处理就演化成了基于事件订阅的消息队列系统了。

本系列教程首发在学院君网站(xueyuanjun.com),你可以点击页面左下角阅读原文链接查看最新更新的教程。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-12-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 极客书房 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
强化学习之不基于模型的控制(五)
-贪婪策略)被提出,其基本思想就是使得某一状态下所有可能的行为都有几率被选中执行,具体通过设置一个比较小的
CristianoC
2021/01/04
8670
强化学习之不基于模型的控制(五)
【DeepMind 公开课-深度强化学习教程笔记04】不基于模型的预测
点击上方“专知”关注获取更多AI知识! 【导读】Google DeepMind在Nature上发表最新论文,介绍了迄今最强最新的版本AlphaGo Zero,不使用人类先验知识,使用纯强化学习,将价值网络和策略网络整合为一个架构,3天训练后就以100比0击败了上一版本的AlphaGo。Alpha Zero的背后核心技术是深度强化学习,为此,专知有幸邀请到叶强博士根据DeepMind AlphaGo的研究人员David Silver《深度强化学习》视频公开课进行创作的中文学习笔记,在专知发布推荐给大家!(关注
WZEARW
2018/04/10
1.2K0
【DeepMind 公开课-深度强化学习教程笔记04】不基于模型的预测
强化学习系列案例 | 蒙特卡洛方法实现21点游戏策略
蒙特卡洛方法(Monte Carlo method)是20世纪40年代中期提出的一种以概率统计为指导的重要数值计算方法。其名字来源于摩洛哥的赌城蒙特卡洛,象征着概率。蒙特卡洛方法在金融工程学,宏观经济学,计算物理学等领域应用广泛。
数据酷客
2020/04/23
2K0
强化学习系列案例 | 蒙特卡洛方法实现21点游戏策略
如何使用强化学习玩21点?
本文将比较分析Monte-Carlo控制算法与时域差分控制算法在解21点(Blackjack)博弈中的应用。
用户7623498
2020/08/04
1.7K0
【AlphaGo Zero 核心技术-深度强化学习教程笔记05】不基于模型的控制
【导读】Google DeepMind在Nature上发表最新论文,介绍了迄今最强最新的版本AlphaGo Zero,不使用人类先验知识,使用纯强化学习,将价值网络和策略网络整合为一个架构,3天训练后就以100比0击败了上一版本的AlphaGo。Alpha Zero的背后核心技术是深度强化学习,为此,专知有幸邀请到叶强博士根据DeepMind AlphaGo的研究人员David Silver《深度强化学习》视频公开课进行创作的中文学习笔记,在专知发布推荐给大家!(关注专知公众号,获取强化学习pdf资料,详情
WZEARW
2018/04/09
8320
【AlphaGo Zero 核心技术-深度强化学习教程笔记05】不基于模型的控制
强化学习决策涉及因素太多,要知道确切的概率几乎不可能?
强化学习已经席卷了整个 AI 世界。从 AlphaGo 到 AlphaStar,由强化学习提供动力的 AI 智能体已经战胜了越来越多由人类主导的传统活动。通过在某一环境中对智能体行为进行优化以实现最大奖励是强化学习的关键,但是绝大多数强化学习方法需要对环境有完整的了解,而现实中这是难以实现的,基于样本的学习方法(例如蒙特卡洛)则可以解决这一痛点。本文以 21 点游戏为例,对蒙特卡洛方法进行了在强化学习中的应用进行了介绍,AI 科技评论编译如下。
AI科技评论
2019/12/05
5620
使用python轻松实现21点小游戏
先来了解一下21点游戏,其实21点是一种流行的纸牌游戏,玩家的目标是使手中的牌总点数尽可能接近21点,但不能超过21点。玩家和电脑轮流抽取牌,可以选择继续抽牌或停止。游戏的策略在于判断何时继续抽牌以及何时停止,以使得自己的点数最接近21点,同时又不超过。本文将使用Python编写一个简单的21点小游戏,让你可以在终端中体验这个有趣的游戏。
三掌柜
2024/03/19
9390
使用python轻松实现21点小游戏
自娱自乐之用python写一个21点游戏
如果大家有需要帮忙推荐的工具、框架、应用、脚本可以在文章下方留言,留言中被点赞、推荐回复较多的,波哥就会帮各位提前安排哦!
IT运维技术圈
2025/01/02
2290
自娱自乐之用python写一个21点游戏
强化学习之蒙特卡洛方法介绍
在强化学习问题中,我们可以用马尔可夫决策过程(MDP)和相关算法找出最优行动值函数 q∗(s,a)和v∗(s),它通过策略迭代和值迭代找出最佳策略。
崔庆才
2019/09/04
1.8K0
强化学习之蒙特卡洛方法介绍
强化学习算法基准测试:6种算法在多智能体环境中的表现实测
强化学习作为机器学习领域的重要分支,通过智能体与环境的交互来学习最优决策策略。在单智能体环境中,智能体面临的是相对静态的环境动态,而多智能体环境则引入了更为复杂的交互机制和竞争关系,这为强化学习算法的设计和评估带来了全新的挑战。
deephub
2025/08/20
2660
强化学习算法基准测试:6种算法在多智能体环境中的表现实测
强化学习读书笔记(5)|蒙特卡洛方法(Monte Carlo Methods)
前面两章都假设我们已知MDP的分布p(s'r|s,a)(model),但有时这一点难以做到,或者说这种Markov假设可能是不合理的,那么我们只能从真实/模拟环境中去获取这些知识。蒙特卡洛方法只需要经验知识,即:来自线上或者模拟环境交互过程的样本序列(包括状态序列、动作序列、奖励序列)。“蒙特卡洛”这个词被广泛用在利用大量随机元素作估计的地方。在这里我们用它来表示基于完全return平均值的方法。
用户1621951
2019/08/26
7520
强化学习读书笔记(5)|蒙特卡洛方法(Monte Carlo Methods)
【强化学习】读书手札:动态规划(DP)&蒙特卡洛(MC)&时序差分(TD)区别
动态规划最主要的特点是转移概率已知,因此可根据贝尔曼方程来进行状态更新,相当于开了“上帝视角”,不适用于实际问题。
zstar
2022/06/14
1.3K0
【强化学习】读书手札:动态规划(DP)&蒙特卡洛(MC)&时序差分(TD)区别
强化学习理论篇
强化学习在DeepSeek-V3发挥了关键作用,现阶段LLM的发展已经离不开 强化学习这一核心技术了,从大模型对齐到推理模型训练再到如今的智能体强化学习(Agentic RL),几乎能AI 的每个领域看到强化学习的身影。
liddytang
2025/06/23
5070
强化学习从基础到进阶-案例与实践[3]:表格型方法:Sarsa、Qlearning;蒙特卡洛策略、时序差分等以及Qlearning项目实战
策略最简单的表示是查找表(look-up table),即表格型策略(tabular policy)。使用查找表的强化学习方法称为表格型方法(tabular method),如蒙特卡洛、Q学习和Sarsa。本章通过最简单的表格型方法来讲解如何使用基于价值的方法求解强化学习问题。
汀丶人工智能
2023/10/11
1.2K0
强化学习从基础到进阶-案例与实践[3]:表格型方法:Sarsa、Qlearning;蒙特卡洛策略、时序差分等以及Qlearning项目实战
AI helps AI -- 强化学习从入门到入门
推荐文章:《使用Python实现深度学习模型:智能食品配送优化》,作者:【Echo_Wish】。
languageX
2024/11/17
8090
AI helps AI -- 强化学习从入门到入门
【Golang语言社区】H5游戏开发--JavaScript学习:21点游戏
一、游戏规则 21点游戏的规则有很多种,我在写这个21点游戏的时候,选取了一种规则,描述如下: 1、游戏共有两名玩家,玩家1(庄家)和玩家2,在我编写的这个21点中,玩家1是电脑,玩家2是你,电脑坐庄。 2、一开始,给你和庄家各发两张牌,你可以看到你的两张牌,庄家的牌一张是明牌,一张是暗牌(暗牌是扣过来的牌,你不知道具体点数是多少)。 3、你和庄家的牌都是从一副牌里发出来的,共计52张(不要大小Joker)。 4、A可以当做1点和11点用,J、Q、K当做10点用,其他牌按面值计算点数。 5、发牌后,你可以选
李海彬
2018/03/27
1.9K0
【Golang语言社区】H5游戏开发--JavaScript学习:21点游戏
详解蒙特卡洛方法:这些数学你搞懂了吗?
之前我们讨论过马尔可夫决策过程(MDP,参阅 https://goo.gl/wVotRL)以及寻找最优的动作-价值函数
机器之心
2018/07/30
1.2K0
详解蒙特卡洛方法:这些数学你搞懂了吗?
强化学习读后感
此学习笔记基础来源于zhoubolei RL(https://github.com/zhoubolei/introRL),以基本概念,基本定理,问题建模,代码实现,新论文的阅读为逻辑展开写的。学习强化学习的过程,会相对漫长。比如:一个假想的学习过程,可能会包含sutton的 complete draft;一些RL基础课程,David Silver,伯克利RL或周博磊等;经典算法的复现;核心研究部门的学术文章(openAI,DeepMind,...);靠谱博士写的博文;会遇见公式符号,上下标,算法实现细节,问题优化,具体问题建模等问题。这里,只是个开始,代码框架可参考PARL。不太懂wx格式,建议wx对latex支持更友好,不要搞什么其他幺蛾子语法。
BBuf
2021/10/08
8400
强化学习(五)用时序差分法(TD)求解
    在强化学习(四)用蒙特卡罗法(MC)求解中,我们讲到了使用蒙特卡罗法来求解强化学习问题的方法,虽然蒙特卡罗法很灵活,不需要环境的状态转化概率模型,但是它需要所有的采样序列都是经历完整的状态序列。如果我们没有完整的状态序列,那么就无法使用蒙特卡罗法求解了。本文我们就来讨论可以不使用完整状态序列求解强化学习问题的方法:时序差分(Temporal-Difference, TD)。
刘建平Pinard
2018/08/30
1.8K0
强化学习(五)用时序差分法(TD)求解
深入探索强化学习:蒙特卡洛策略评估的偏差-方差权衡与访问法统计特性对比
在人工智能的第三次浪潮中,强化学习(Reinforcement Learning)已成为解决序列决策问题的核心范式。与监督学习不同,强化学习通过智能体与环境的交互来学习最优策略,这种"试错-反馈"机制使其在2025年的机器人控制、游戏AI和自动驾驶等领域展现出独特优势。作为强化学习算法家族的重要成员,蒙特卡洛方法因其直观性和无模型特性,在策略评估环节发挥着不可替代的作用。
用户6320865
2025/08/27
2730
深入探索强化学习:蒙特卡洛策略评估的偏差-方差权衡与访问法统计特性对比
推荐阅读
相关推荐
强化学习之不基于模型的控制(五)
更多 >
交个朋友
加入腾讯云官网粉丝站
蹲全网底价单品 享第一手活动信息
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档