[嵌入式系统-23]:RT-Thread -10- 内核组件编程接口 - 线程与线程间通信:事件、邮箱、消息队列

目录

一、线程间通信机制

1.1 概述

1.2 RT-Thread 邮箱与事件的区别

1.3 RT-Thread 邮箱与消息队列比较

二、事件

2.1 概述

2.2 使用方法

2.3 代码示例

三、邮箱(Mailbox)

3.1 概述

3.2 使用方法

四、消息队列

4.1 概述

4.2 使用方法


一、线程间通信机制

1.1 概述

在 RT-Thread 中,线程间通信可以通过以下几种机制实现

  1. 全局变量:线程可以通过访问共享的全局变量进行通信。但是需要注意的是,多个线程同时访问全局变量可能引发竞争条件,因此需要使用信号量或其他同步机制来确保数据的一致性和正确性

  2. 队列:队列是一种常见的线程间通信机制,可以使用消息队列或邮箱来传递数据。一个线程将消息发送到队列中,其他线程则可以从队列中接收这些消息。RT-Thread 提供了 rt_mq_t 和 rt_mailbox_t 用于创建消息队列和邮箱对象,并提供了相应的 API 函数进行消息的发送和接收操作。

  3. 信号量:信号量可以用于线程间的同步和通信。一个线程可以通过获取信号量来等待某个事件的发生,而其他线程可以通过释放信号量来触发这个事件。RT-Thread 提供了 rt_sem_t 用于创建信号量对象,并提供了相应的 API 函数进行信号量的等待和释放操作。

  4. 事件标志组:事件标志组是一种更为复杂的线程间通信机制,用于处理复杂的多线程同步和通信场景。一个线程可以通过等待特定的事件标志来等待某个事件的发生,而其他线程则可以激活这些事件标志,以触发相应的事件。RT-Thread 提供了 rt_ef_t 用于创建事件标志组对象,并提供了相应的 API 函数进行事件标志的等待和设置操作。

这些机制可以根据具体的需求进行选择和组合使用,以实现线程间的有效通信和同步。需要注意的是,使用这些机制时应当考虑线程间的安全性和正确性,避免发生竞争条件和死锁等问题。另外,还要注意使用适当的同步机制和处理机制来确保线程间的通信是可靠和高效的。具体用法和函数接口可以参考 RT-Thread 的官方文档和 API 参考手册。

1.2 RT-Thread 邮箱与事件的区别

RT-Thread邮箱和事件在功能和适用场景上有一些区别:

  1. 功能:

    • 邮箱用于线程间传递和共享数据,可以发送和接收消息。
    • 事件用于线程同步和通知,可以等待和触发事件。
  2. 适用场景:

    • 邮箱适合于需要线程间传递数据的场景,比如线程之间传递控制命令或传输数据块。
    • 事件适合于线程间的同步和通知,特别适合多个线程等待某个共享资源的就绪状态。
  3. 使用方式:

    • 邮箱可以通过发送和接收消息来实现数据的传递,发送和接收可以是同步的或异步的。发送消息时,如果邮箱已满,发送线程可能会被阻塞或等待一段时间。
    • 事件可以通过设置和清除事件标志位来触发和等待事件,等待事件时可以选择阻塞等待,等待结束后,线程会被唤醒。

需要根据具体的应用场景选择使用RT-Thread邮箱或事件。如果只是简单的数据传递,可以选择使用邮箱;如果需要线程间同步和通知,例如等待共享资源就绪,可以选择使用事件。在设计线程间通信时,要合理设置同步机制,以避免死锁、竞态条件等问题的发生。

1.3 RT-Thread 邮箱与消息队列比较

RT-Thread 中的邮箱(Mailbox)和消息队列(Message Queue)都是线程间通信的机制,它们在功能和使用方法上有一些区别。

邮箱(Mailbox)是一种点对点Point-to-Point)的线程间通信机制。它允许一个线程发送一个消息给另一个线程,并且一个邮箱只能有一个消息接收者。当发送消息时,如果邮箱已经被占用,则发送线程会被阻塞(或进入超时等待状态)直到消息被接收。对于接收线程来说,可以选择阻塞等待邮箱中有新的消息到达,或者立即返回邮箱中的第一个消息。RT-Thread 提供的 rt_mailbox_t 对象以及相应的 API 函数可以用于创建和使用邮箱。

消息队列(Message Queue)是一种多对多(Many-to-Many)的线程间通信机制。它允许多个线程同时发送和接收消息。当发送消息时,如果消息队列已满,则发送线程会被阻塞(或进入超时等待状态)直到消息队列有空闲空间。对于接收线程来说,可以选择阻塞等待消息队列中有新的消息到达,或者立即返回消息队列中的第一个消息。RT-Thread 提供的 rt_mq_t 对象以及相应的 API 函数可以用于创建和使用消息队列。

邮箱和消息队列之间的选择主要取决于以下几个方面:

  1. 通信模式:如果只有一个发送者和一个接收者之间的点对点通信需求,则可以使用邮箱。如果需要多个发送者和多个接收者之间的多对多通信需求,则应使用消息队列。

  2. 缓冲空间:邮箱的缓冲空间是固定的,只能容纳一个消息。消息队列的缓冲空间可以根据需求进行配置,可以容纳多个消息

  3. 阻塞与非阻塞:邮箱和消息队列都支持阻塞和非阻塞模式。在阻塞模式下,发送者和接收者可能被阻塞,直到指定条件满足。在非阻塞模式下,发送者和接收者会立即返回,并根据实际情况处理后续逻辑。

  4. 功能复杂度:消息队列相对于邮箱来说,功能更为丰富,可以在消息中携带更多的数据和信息,并且可以设置消息的优先级、超时等待等特性。

根据具体的应用场景和需求来选择合适的线程间通信机制。在使用时,需要考虑到通信的需求、资源消耗、实时性等因素,并确保使用的机制能够满足应用的要求。

二、事件

2.1 概述

在RT-Thread中,事件是一种线程间通信的机制,用于实现线程的同步和消息传递

RT-Thread提供了一组事件相关的API函数,可以用于创建、设置、等待和清除事件,以及检查事件状态。

下面是一些常用的RT-Thread事件相关的API函数:

  1. rt_event_init:初始化事件对象。使用该函数可以初始化一个事件对象,设置初始的事件状态。

  2. rt_event_send:发送事件。使用该函数可以向指定的事件对象发送一个或多个事件标志,唤醒等待这些事件的线程。

  3. rt_event_recv:接收事件。使用该函数可以等待指定的事件标志出现,并阻塞当前线程,直到事件被发送或超时。

  4. rt_event_control:控制事件。使用该函数可以对事件对象进行设置或查询操作,如获取当前事件状态、清除事件标志等

  5. rt_event_detach:分离事件。使用该函数可以将一个事件对象与当前线程解绑,解除线程对该事件的等待。

通过使用事件,在多线程的应用中可以实现线程的同步和通信,以便在需要的时候唤醒或阻塞线程,有效地控制线程的执行顺序和并发行为。

t_event_t 数据结构是一个包含了事件标志和等待队列等信息的结构体。它通过对事件标志的设置和清除来表示事件的触发和等待状态。当某个线程等待事件时,它可以使用 rt_event_recv 函数阻塞等待事件的触发,并在事件触发时被唤醒。同时,其他线程可以使用 rt_event_send 函数来发送事件,触发等待中的线程继续执行。

事件对象是一种基于标志位的同步机制,它可以用于线程间同步、线程间通信以及线程和中断之间的通信。在事件对象中,并不涉及到消息队列的概念。如果您需要使用消息队列进行线程间通信,可以考虑使用 RT-Thread 的消息队列功能。

总结来说,rt_event_t 的内部数据结构是一个用于线程同步和通信的事件对象,它并不是一个消息队列。

接收事件的线程和发送线程通过event事件队列进行通信。

2.2 使用方法

在RT-Thread中,使用事件进行线程间同步和通信可以按照以下步骤进行:

  1. 定义事件对象:
    在需要使用事件的线程中,首先定义一个事件对象,可以使用 rt_event_t 类型的变量来表示。例如:

    rt_event_t event;
    
  2. 初始化事件对象:
    在线程初始化的时候,使用 rt_event_init 函数对事件对象进行初始化。例如:

    rt_event_init(&event, "my_event", RT_IPC_FLAG_FIFO);
    
  3. 线程等待事件:
    在需要等待某个特定事件发生的线程中,可以使用 rt_event_recv 函数来等待事件。该函数会阻塞当前线程,直到事件发生或超时。例如:

    rt_uint32_t set_events = RT_EVENT_FLAG0 | RT_EVENT_FLAG1;  // 指定需要等待的事件标志,是32bits的整型数据
    rt_uint32_t recv_events;  // 存储接收到的事件标志
    
    rt_event_recv(&event, set_events, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,
                  RT_WAITING_FOREVER, &recv_events);
    
  4. 发送事件:
    在某个线程中发生了需要通知其他线程的事件时,可以使用 rt_event_send 函数向指定的事件对象发送事件。例如:

    rt_uint32_t set_events = RT_EVENT_FLAG0 | RT_EVENT_FLAG2;  // 指定要发送的事件标志
    
    rt_event_send(&event, set_events);
    
  5. 控制事件:
    可以使用 rt_event_control 函数来对事件对象进行设置或查询操作。例如,可以使用它来获取当前事件的状态、清除事件标志等。例如:

    rt_uint32_t current_events;
    
    rt_event_control(&event, RT_IPC_CMD_EVENT_QUERY, &current_events);
    

这些是事件在RT-Thread中的基本使用方法。需要根据具体的应用场景和需求,结合更多的RT-Thread事件相关函数和选项来灵活地管理和控制事件的触发和处理。

请注意,以上示例代码只是演示了基本的事件使用方法,实际使用时需要根据具体的应用场景和需求进行适当的调整和扩展。

在RT-Thread中,有一些宏定义用于控制事件对象的行为和属性。以下是一些常见的宏定义:

  1. RT_IPC_FLAG_FIFO:
    这个宏定义用于指定事件对象的类型,表示使用先进先出(FIFO)方式处理事件。它可以与 rt_event_init 函数的第三个参数进行组合使用,以确定事件类型。

  2. RT_EVENT_FLAG_OR:
    这个宏定义用于在使用 rt_event_recv 函数等待事件时,指定事件标志的匹配逻辑。当被等待的事件中任意一个标志位被触发时,等待将会结束。如果不使用该宏定义,则表示需要等待所有的事件标志被触发。

  3. RT_EVENT_FLAG_CLEAR:
    这个宏定义用于设置 rt_event_recv 函数等待事件时,指定是否在接收到事件后自动清除已触发的事件标志。如果使用该宏定义,则接收到的事件将会被从事件对象中清除。

  4. RT_IPC_CMD_EVENT_QUERY:
    这个宏定义用于在使用 rt_event_control 函数时,指定查询事件对象的操作。使用它可以获得当前事件对象的状态,包括当前事件标志的值。

这些宏定义是RT-Thread中用于事件对象相关操作的一部分,通过它们可以根据不同的需求来设置和控制事件的行为。具体的宏定义可能会根据不同的RT-Thread版本有所差异,您可以查阅RT-Thread的官方文档或使用手册,获取更详细的信息和最新的宏定义列表。

2.3 代码示例

下面是一个简单的代码示例,展示了如何使用RT-Thread的事件对象进行线程间同步和通信:

#include <rtthread.h>

#define EVENT_FLAG_1    (1 << 0)
#define EVENT_FLAG_2    (1 << 1)
#define EVENT_FLAG_3    (1 << 2)

static rt_event_t event;

static void thread1_entry(void *param)
{
    rt_thread_delay(100);  // 延迟一段时间,确保 thread2 优先运行

    rt_kprintf("Thread 1 triggered event EVENT_FLAG_1\n");
    rt_event_send(&event, EVENT_FLAG_1);

    rt_thread_delay(1000);  // 延迟一段时间,等待 thread2 运行完成
    
    rt_kprintf("Thread 1 triggered event EVENT_FLAG_2\n");
    rt_event_send(&event, EVENT_FLAG_2);
}

static void thread2_entry(void *param)
{
    rt_uint32_t recv_event;
    
    rt_event_recv(&event, EVENT_FLAG_1, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,
                  RT_WAITING_FOREVER, &recv_event);
    rt_kprintf("Thread 2 received event EVENT_FLAG_1\n");

    rt_thread_delay(500);  // 延迟一段时间,确保 thread1 已发送 EVENT_FLAG_2

    rt_event_recv(&event, EVENT_FLAG_2, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,
                  RT_WAITING_FOREVER, &recv_event);
    rt_kprintf("Thread 2 received event EVENT_FLAG_2\n");
}

int rt_application_init(void)
{
    rt_thread_t thread1, thread2;

    rt_event_init(&event, "my_event", RT_IPC_FLAG_FIFO);

    thread1 = rt_thread_create("thread1", thread1_entry, RT_NULL,
                               1024, 10, 10);
    if (thread1 != RT_NULL)
        rt_thread_startup(thread1);

    thread2 = rt_thread_create("thread2", thread2_entry, RT_NULL,
                               1024, 20, 10);
    if (thread2 != RT_NULL)
        rt_thread_startup(thread2);
    
    return 0;
}

在上述代码中,首先定义了一个事件对象 event。然后在线程1(thread1)中,它先触发了 EVENT_FLAG_1 事件,然后通过延迟一段时间后触发了 EVENT_FLAG_2 事件。在线程2(thread2)中,它等待接收 EVENT_FLAG_1 事件,并在接收到事件后打印一条消息,然后延迟一段时间后再等待接收 EVENT_FLAG_2 事件,并在接收到事件后打印另一条消息。

通过这样的事件触发和等待机制,两个线程能够实现同步和通信,通过事件对象进行线程间的消息传递。

请注意,以上示例代码仅展示了基本的事件使用方法,并没有涉及到更复杂的事件处理逻辑,实际使用时需要根据具体的应用场景和需求进行适当的调整和扩展。同时,RT-Thread的版本和配置可能会有所不同,所以建议查阅相关文档以获取更多具体的信息和示例代码。

三、邮箱(Mailbox)

3.1 概述

在 RT-Thread 中,邮箱(Mailbox)是一种基于消息队列的线程间通信机制,用于在多个线程之间传递数据块。它可以实现一对一或多对一线程通信

邮箱具有以下几个特点:

  1. 数据块传递:邮箱用于传递数据块,每个数据块可以是任意大小的用户数据。

  2. 单个接收者邮箱通常只有一个接收者,该接收者可以从邮箱中获取数据块,并进行处理。

  3. 缓冲空间:邮箱提供了一定大小的缓冲空间,用户可以在创建邮箱时指定该空间大小。当发送者向邮箱发送数据块时,如果邮箱已满,发送者将被阻塞直到邮箱有足够的空间。相反,接收者从邮箱中获取数据块时,如果邮箱为空,接收者将被阻塞直到有数据可用。

  4. 阻塞和非阻塞:发送者和接收者可以选择阻塞或非阻塞模式进行操作。在阻塞模式下,线程在发送或接收数据块时会被阻塞,直到条件满足。而在非阻塞模式下,线程将立即返回,不会等待。

使用邮箱的主要优点是提供了一种有序的数据传递机制,能够有效管理数据的发送和接收。邮箱可以在任务之间传递复杂的数据结构,帮助实现更复杂的线程间通信场景。

在 RT-Thread 中,邮箱由 rt_mailbox_t 数据类型表示,并提供了一系列的 API 函数来创建、发送、接收和管理邮箱。使用这些 API 函数,可以在 RT-Thread 中方便地使用邮箱实现线程间的数据交换和协调。

3.2 使用方法

RT-Thread 提供了 rt_mailbox_t 数据类型来表示邮箱,并提供了一系列函数来操作邮箱,包括创建邮箱、发送消息、接收消息等。下面是邮箱的基本使用方法:

  1. 创建邮箱:可以使用 rt_mb_create() 函数来创建邮箱。

    #include <rtthread.h>
    
    static rt_mailbox_t mb;
    
    int app_mysample_main(void)
    {
        mb = rt_mb_create("my_mailbox", 16, RT_IPC_FLAG_FIFO);
        if (mb == RT_NULL)
        {
            rt_kprintf("Failed to create mailbox.\n");
            return -1;
        }
    
        return 0;
    }
    

    在上述示例中,我们通过 rt_mb_create() 函数创建了一个名为 “my_mailbox” 的邮箱,指定了邮箱的容量为 16,并设置了邮箱的属性为 FIFO(先进先出)。

  2. 发送消息:当需要发送消息到邮箱时,可以使用 rt_mb_send() 函数。

    rt_err_t rt_mb_send(rt_mailbox_t mb, rt_uint32_t value);
    

    mb 是邮箱对象,value 是要发送的消息。函数的返回值为操作结果,如果成功则返回 RT_EOK

    #include <rtthread.h>
    
    void thread_entry(void* parameter)
    {
        if (rt_mb_send(mb, 100) != RT_EOK)
        {
            rt_kprintf("Failed to send message.\n");
        }
    
        rt_thread_delay(100); // 延时一段时间
    }
    

    在上述示例中,我们通过 rt_mb_send() 函数向邮箱 mb 发送了一个数值为 100 的消息。

  3. 接收消息:当需要接收邮箱中的消息时,可以使用 rt_mb_recv() 函数。

    rt_err_t rt_mb_recv(rt_mailbox_t mb, rt_uint32_t* value, rt_int32_t timeout);
    

    mb 是邮箱对象;value 是用于存储接收到的消息的变量的地址;timeout 是接收消息的超时时间,如果设置为 0,则表示立即返回,如果设置为 RT_WAITING_FOREVER,则表示永远等待直到有消息可接收。函数的返回值为操作结果,如果成功则返回 RT_EOK

    #include <rtthread.h>
    
    void thread_entry(void* parameter)
    {
        rt_uint32_t value;
    
        if (rt_mb_recv(mb, &value, RT_WAITING_FOREVER) == RT_EOK)
        {
            rt_kprintf("Received message: %d\n", value);
        }
    }
    

    在上述示例中,我们通过 rt_mb_recv() 函数从邮箱 mb 中接收消息,并将接收到的消息存储在 value 变量中。

使用邮箱进行线程间通信时需要注意以下几点:

  • 邮箱的容量是有限的,当邮箱已满时,发送线程将会被阻塞,直到有空间可以发送消息。
  • 当邮箱为空时,接收线程将会被阻塞,直到有消息可接收。
  • 发送线程和接收线程可以处于不同的优先级。
  • 邮箱的消息可以是任意类型的数据。

通过合理的使用邮箱,可以实现线程间的异步通信,实现消息的传递和数据的共享。

rt_mb_send 是 RT-Thread 提供的函数之一,用于向邮箱(Mailbox)发送数据块。

函数原型为:

rt_err_t rt_mb_send(rt_mailbox_t mb, rt_ubase_t value);

参数说明:

  • mb:要发送数据块的邮箱句柄,通过 rt_mb_create 或其他方式创建得到。
  • value:要发送的值,通常是一个参数或标识符。

返回值为 rt_err_t 类型,表示函数执行的结果,一般返回以下值之一:

  • RT_EOK:发送成功。
  • RT_EEMPTY:邮箱为空,无法发送数据块。
  • RT_EFULL:邮箱已满,无法发送数据块。
  • 其他错误码,表示发送过程中的其他错误。

示例代码:

rt_mailbox_t mb;      // 邮箱句柄

mb = rt_mb_create("my_mailbox", 10, RT_IPC_FLAG_FIFO);   // 创建大小为 10 的邮箱

if (rt_mb_send(mb, 123) == RT_EOK)
{
    // 数据块发送成功
    // ...
}
else
{
    // 数据块发送失败
    // ...
}

在上述示例中,通过 rt_mb_create 创建了一个名为 “my_mailbox” 大小为 10 的邮箱。然后,使用 rt_mb_send 函数向该邮箱发送值为 123 的数据块。如果发送成功,函数返回值为 RT_EOK,可以执行相应的操作;否则,返回值表示发送失败的原因。

需要注意的是,发送数据块时可能会阻塞线程,具体是否阻塞取决于邮箱的状态以及是否设置了阻塞标志。可以参考 RT-Thread 的官方文档和 API 参考手册,了解更多关于 rt_mb_send 函数的详细信息。

四、消息队列

4.1 概述

RT-Thread 中的消息队列(Message Queue)是一种线程间通信的机制,用于实现多对多的消息传递。它可以在实时操作系统中的不同线程之间传递数据和信息。

消息队列有以下几个关键特点:

  1. 多对多通信:消息队列允许多个发送者和多个接收者之间进行通信。发送者可以同时发送消息到同一个消息队列,而接收者可以从消息队列中获取消息。

  2. 缓冲空间:消息队列提供了一定大小的缓冲空间,用于存储消息。发送者可以将消息写入队列中,而接收者则可以从队列中读取消息。该缓冲空间的大小是可以根据需求进行配置的。

  3. 先进先出(FIFO):消息队列通常使用先进先出的策略进行消息的发送和接收。也就是说,先发送的消息会先被接收,后发送的消息会在队列中排队等待被接收。

  4. 阻塞和非阻塞:发送者和接收者可以选择阻塞或非阻塞模式来进行操作。在阻塞模式下,如果发送者试图向已满的队列发送消息,或接收者试图从空队列中接收消息,线程将被阻塞直到条件满足。而在非阻塞模式下,如果无法立即完成发送或接收操作,线程将立即返回,而不会等待。

  5. 大小和类型:消息队列可以存储不同大小和类型的消息。在创建消息队列时,需要指定每个消息的大小。

使用消息队列的主要优点是它可以有效地进行线程间通信,使得不同线程之间的数据传递更加灵活和可靠。通过将数据封装为消息并发送到队列中,可以实现数据的异步传输和解耦,提高系统的可维护性和扩展性。

在 RT-Thread 中,消息队列由 rt_mq_t 数据类型表示,并提供了一系列的 API 函数用于创建、发送、接收和管理消息队列。使用这些 API 函数,可以方便地在 RT-Thread 中使用消息队列实现线程间的通信和数据交换

4.2 使用方法

在 RT-Thread 中,消息队列(Message Queue)是一种用于线程间通信的机制,用于实现多对多的消息传递。RT-Thread 提供了 rt_mq_t 结构体和一系列的 API 函数来创建和操作消息队列。

要创建一个消息队列,可以使用 rt_mq_create() 函数,该函数接受两个参数:队列的大小和每个消息的大小。例如:

rt_mq_t mq;
void *buffer[10];

mq = rt_mq_create("myqueue", sizeof(buffer) / sizeof(buffer[0]), sizeof(void *), RT_IPC_FLAG_FIFO);

这将创建一个名为 “myqueue” 的消息队列,队列的大小为 10,每个消息的大小为一个指针的大小。RT_IPC_FLAG_FIFO 参数表示使用先进先出(FIFO)模式。

发送消息到队列中使用 rt_mq_send() 函数,该函数接受消息队列对象、消息地址和超时时间作为参数。例如:

void *msg = &data;
rt_err_t result;
result = rt_mq_send(mq, &msg, sizeof(msg), RT_WAITING_FOREVER);

这将把 msg 的地址发送到消息队列 mq 中,消息的大小为一个指针的大小。RT_WAITING_FOREVER 表示等待时间为无限大。

从队列中接收消息使用 rt_mq_recv() 函数,该函数接受消息队列对象、消息地址、消息大小和超时时间作为参数。例如:

void *msg;
unsigned int size;
rt_err_t result;
result = rt_mq_recv(mq, &msg, &size, RT_WAITING_FOREVER);

这将从消息队列 mq 中接收一条消息,并将其存储在 msg 中,获取消息的大小存储在 size 中。

除了上述基本的发送和接收操作外,还可以使用其他 API 函数来管理消息队列,如获取当前队列中的消息数量、清空队列等。

需要注意的是,对于发送和接收操作,RT-Thread 提供了阻塞和非阻塞两种模式。使用阻塞模式时,如果发送或接收操作不能立即完成,线程将被阻塞直到操作完成或超时。而使用非阻塞模式时,如果发送或接收操作不能立即完成,将立即返回。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/393812.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

代码随想录第33天|● 1005.K次取反后最大化的数组和 ● 134. 加油站 ● 135. 分发糖果

文章目录 1005.K次取反后最大化的数组和贪心思路&#xff1a;代码&#xff1a; 34. 加油站思路一&#xff1a;全局贪心代码&#xff1a; 思路二&#xff1a;代码&#xff1a; 135. 分发糖果思路&#xff1a;两边考虑代码&#xff1a; 1005.K次取反后最大化的数组和 贪心思路&am…

Stackoverflow(1)-根据RequestBody的内容来区分使用哪个资源

如果使用Spring&#xff0c;可以通过RequestBody将请求体的json转换为Java对象&#xff0c;但如果URI相同&#xff0c;而请求体的内容不同&#xff0c;应该怎么办&#xff1f;问题来源(stackoverflow)&#xff1a;Spring RequestBody without using a pojo?稍微研究了一下&…

手把手一起开发SV4E-I3C设备(三)

JEDEC DDR5 SPD Hub Devices例程 为进一步方便程序开发&#xff0c;使用vscode开发程序&#xff0c;IntrospectESP_23.3.0软件调用运行&#xff0c;如图所示&#xff0c;双击UTILITY区域的PythonModule&#xff0c;自动生成一个py文件&#xff0c;可以给该模块重命名。例如重命…

重学Java 16.面向对象.4.对象数组

一、对象数组 1.需求&#xff1a;定义一个长度为3的数组&#xff0c;存储3个person对象&#xff0c;遍历数组&#xff0c;将三个person对象中的属性值获取出来 public class Person {private String name;private int age;public Person(String name,int age) {this.name name…

Yii2项目使用composer异常记录

问题描述 在yii2项目中&#xff0c;使用require命令安装依赖时&#xff0c;出现如下错误提示 该提示意思是&#xff1a;composer运行时&#xff0c;执行了yiisoft/yii2-composer目录下的插件&#xff0c;但是该插件使用的API版本是1.0&#xff0c;但是当前的cmposer版本提供的…

单体工程结构

本文主要说明下单体项目的工程结构如何设计&#xff0c;目前业界存在两种主流的应用工程结构&#xff1a;一种是阿里推出的《Java开发手册》中推荐的&#xff0c;另外一种是基于DDD(领域驱动设计)推荐的。下面我们来看下两种工程结构是怎样的。 一、 基于阿里《Java开发手册》…

生成式 AI - Diffusion 模型的数学原理(3)

来自 论文《 Denoising Diffusion Probabilistic Model》&#xff08;DDPM&#xff09; 论文链接&#xff1a; https://arxiv.org/abs/2006.11239 Hung-yi Lee 课件整理 文章目录 一、图像生成模型本质上的共同目标二、最大似然估计三、和VAE的关联四、概率计算 一、图像生成模…

三维模型优化与可视化开发者服务

一站式服务开发者 1、极速流畅的浏览体验 无需安装插件&#xff0c;实现模型多端展示 最大支持100G模型&#xff0c;杜绝花、卡、闪 2、丰富易用的开发工具 无需掌握图形技术&#xff0c;实现模型轻量化和3D交互展示 提供丰富的SDK和API&#xff0c;简洁易用 老子云API 提供…

Java+SpringBoot:高校竞赛管理新篇章

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

添加环境变量

目录 一、前言二、目的三、添加环境变量的步骤四、检查环境变量是否配置成功 一、前言 在很多地方在下载完软件后都需要添加环境变量方可使用。这里以要在终端使用MySQL为例来说一下&#xff0c;在安装好MySQL8.0版本的前提下&#xff0c;如何添加环境变量。 二、目的 添加环…

力扣OJ题——旋转数组

题目&#xff1a;189.旋转数组 给定一个整数数组 nums&#xff0c;将数组中的元素向右轮转 k 个位置&#xff0c;其中 k 是非负数 思路一&#xff1a; 1.每次挪动旋转1位&#xff08;用tmp将最后一位存起来&#xff0c;其余所有数据向后移&#xff0c;然后将tmp放在第一个位…

单源最短路径(Dijkstra)

前言 dijkstra&#xff1a;对于无负边的情况下可以达到O(nlogn)且很难被卡 最短路 - OI Wiki (oi-wiki.org) P3371 【模板】单源最短路径&#xff08;弱化版&#xff09; P3371 【模板】单源最短路径&#xff08;弱化版&#xff09; - 洛谷 | 计算机科学教育新生态 (luogu.com…

2.18学习总结

链式前向星的处理和建立 tarjan对割点和缩点的使用 拓扑排序 链式前向星&#xff1a; 预处理&#xff1a; struct edge{int from;int to;int next; }e[N]; int n,m,head[N],dfn[N],low[N],tot,color[N],num[N],out[N],s,instack[N],id; 处理&#xff1a; void add(int …

svg之全局组件,配合雪碧图解决vue2的svg优化问题

这里是vue2中的svg的完整解决方案的另一篇。 <template><svg :class"svgClass"><use :xlink:href"#${name}"></use></svg> </template><script>export default {name: icon,props: {name: {type: String,requi…

lv15 input子系统框架、外设驱动开发 5

一、input子系统基本框架 在我们日常的Linux系统中&#xff0c;存在大量的输入设备&#xff0c;例如按键、鼠标、键盘、触摸屏、摇杆等&#xff0c;他们本身就是字符设备&#xff0c;linux内核将这些字符设备的共同性抽象出来&#xff0c;简化驱动开发建立了一个input子系统。 …

关于Spring Boot应用系统避免因为日切(日期切换)导致请求结果变更的一种解决方案

一、前言 在系统开发过程中&#xff0c;有些业务功能面临日切&#xff08;日期切换&#xff09;问题&#xff0c;比如结息跑批问题&#xff0c;在当前工作日临近24点的时候触发结息&#xff0c;实际交易时间我们预期的是当前时间&#xff0c;但是由于业务执行耗时&#xff0c;…

【EI会议征稿通知】第五届城市工程与管理科学国际会议(ICUEMS 2024)

【Scopus稳定检索】第五届城市工程与管理科学国际会议&#xff08;ICUEMS 2024&#xff09; 2024 5th International Conference on Urban Engineering and Management Science 第五届城市工程与管理科学国际会议&#xff08;ICUEMS 2024&#xff09;将于2024年5月31日-6月2日…

告警能力中台设计与实践(三)——告警通知

一、告警消息与告警通知 1、告警消息 正如笔者在最开始所写的那样&#xff0c;第三方服务通过调用能力中台的OpenAPI实现告警发起&#xff0c;并且每一次的告警请求都会创建、归档为一条告警消息&#xff08;AlarmMsg&#xff09;。 这样的消息是无状态的&#xff0c;并且对…

Python:变量与数据类型

目录 一、变量 1.1 强数据类型与弱数据类型 1.2 全局函数 1.3 变量的命名规范 二、数据类型 2.1 基本数据类型 2.2 复合数据类型&#xff08;引用数据类型&#xff09; 三、数据类型转换 一、变量 变量&#xff1a;顾名思义&#xff0c;变化的量。在python中代指运行时…

【Java面试】MongoDB

目录 1、mongodb是什么&#xff1f;2、mongodb特点什么是NoSQL数据库&#xff1f;NoSQL和RDBMS有什么区别&#xff1f;在哪些情况下使用和不使用NoSQL数据库&#xff1f;NoSQL数据库有哪些类型?启用备份故障恢复需要多久什么是master或primary什么是secondary或slave系列文章版…