GD32F30X-RT-Thread学习-线程管理

1. 软硬件平台

  1. GD32F307E-START Board开发板
  2. MDK-ARM Keil
    在这里插入图片描述

2.RT-Thread Nano

在这里插入图片描述

3.RT-Thread 内核学习-线程管理

​ 在多线程操作系统中,可以把一个复杂的应用分解成多个小的、可调度的、序列化的程序单元,当合理地划分任务并正确地执行时,这种设计能够让系统满足实时系统的性能及时间的要求。在多线程实时系统中,可以将这个任务分解成子任务。

​ 在 RT-Thread 中,与上述子任务对应的程序实体就是线程线程是实现任务的载体,它是 RT-Thread 中最基本的调度单位,它描述了一个任务执行的运行环境,也描述了这个任务所处的优先等级,重要的任务可设置相对较高的优先级,非重要的任务可以设置较低的优先级,不同的任务还可以设置相同的优先级,轮流运行。(RT-Thread 中Thread 对应于FreeRTOS中的task)。

当线程运行时,它会认为自己是以独占 CPU 的方式在运行,线程执行时的运行环境称为上下文,具体来说就是各个变量和数据,包括所有的寄存器变量、堆栈、内存信息等

​ RT-Thread 线程管理的主要功能是对线程进行管理和调度,系统中总共存在两类线程,分别是系统线程和用户线程,**系统线程是由 RT-Thread 内核创建的线程,用户线程是由应用程序创建的线程,**这两类线程都会从内核对象容器中分配线程对象,当线程被删除时,也会被从对象容器中删除,如下图所示,每个线程都有重要的属性,如线程控制块、线程栈、入口函数等。

在这里插入图片描述

RT-Thread 的线程调度器是抢占式的,主要的工作就是从就绪线程列表中查找最高优先级线程,保证最高优先级的线程能够被运行,最高优先级的任务一旦就绪,总能得到 CPU 的使用权。

当一个运行着的线程使一个比它优先级高的线程满足运行条件,当前线程的 CPU 使用权就被剥夺了,或者说被让出了,高优先级的线程立刻得到了 CPU 的使用权。

如果是中断服务程序使一个高优先级的线程满足运行条件,中断完成时,被中断的线程挂起,优先级高的线程开始运行。

当调度器调度线程切换时,先将当前线程上下文保存起来,当再切回到这个线程时,线程调度器将该线程的上下文信息恢复。

重要概念1 线程控制块

​ 在 RT-Thread 中,线程控制块由结构体 struct rt_thread 表示,线程控制块是操作系统用于管理线程的一个数据结构,它会存放线程的一些信息,例如优先级、线程名称、线程状态等,也包含线程与线程之间连接用的链表结构,线程等待事件集合等,详细定义如下:

/* 线程控制块 */
struct rt_thread
{
    /* rt 对象 */
    char        name[RT_NAME_MAX];     /* 线程名称 */
    rt_uint8_t  type;                   /* 对象类型 */
    rt_uint8_t  flags;                  /* 标志位 */

    rt_list_t   list;                   /* 对象列表 */
    rt_list_t   tlist;                  /* 线程列表 */

    /* 栈指针与入口指针 */
    void       *sp;                      /* 栈指针 */
    void       *entry;                   /* 入口函数指针 */
    void       *parameter;              /* 参数 */
    void       *stack_addr;             /* 栈地址指针 */
    rt_uint32_t stack_size;            /* 栈大小 */

    /* 错误代码 */
    rt_err_t    error;                  /* 线程错误代码 */
    rt_uint8_t  stat;                   /* 线程状态 */

    /* 优先级 */
    rt_uint8_t  current_priority;    /* 当前优先级 */
    rt_uint8_t  init_priority;        /* 初始优先级 */
    rt_uint32_t number_mask;

    ......

    rt_ubase_t  init_tick;               /* 线程初始化计数值 */
    rt_ubase_t  remaining_tick;         /* 线程剩余计数值 */

    struct rt_timer thread_timer;      /* 内置线程定时器 */

    void (*cleanup)(struct rt_thread *tid);  /* 线程退出清除函数 */
    rt_uint32_t user_data;                      /* 用户数据 */
};

其中 init_priority 是线程创建时指定的线程优先级,在线程运行过程当中是不会被改变的(除非用户执行线程控制函数进行手动调整线程优先级)。**cleanup 会在线程退出时,被空闲线程回调一次以执行用户设置的清理现场等工作。**最后的一个成员 user_data 可由用户挂接一些数据信息到线程控制块中,以提供一种类似线程私有数据的实现方式。

重要概念2 线程状态

线程运行的过程中,同一时间内只允许一个线程在处理器中运行,从运行的过程上划分,线程有多种不同的运行状态,如初始状态、挂起状态、就绪状态等。在 RT-Thread 中,线程包含五种状态,操作系统会自动根据它运行的情况来动态调整它的状态。 RT-Thread 中线程的五种状态,如下表所示:

状态描述
初始状态当线程刚开始创建还没开始运行时就处于初始状态;在初始状态下,线程不参与调度。此状态在 RT-Thread 中的宏定义为 RT_THREAD_INIT
就绪状态在就绪状态下,线程按照优先级排队,等待被执行;一旦当前线程运行完毕让出处理器,操作系统会马上寻找最高优先级的就绪态线程运行。此状态在 RT-Thread 中的宏定义为 RT_THREAD_READY
运行状态线程当前正在运行。在单核系统中,只有 rt_thread_self() 函数返回的线程处于运行状态;在多核系统中,可能就不止这一个线程处于运行状态。此状态在 RT-Thread 中的宏定义为 RT_THREAD_RUNNING
挂起状态也称阻塞态。它可能因为资源不可用而挂起等待,或线程主动延时一段时间而挂起。在挂起状态下,线程不参与调度。此状态在 RT-Thread 中的宏定义为 RT_THREAD_SUSPEND
关闭状态当线程运行结束时将处于关闭状态。关闭状态的线程不参与线程的调度。此状态在 RT-Thread 中的宏定义为 RT_THREAD_CLOSE

RT-Thread 提供一系列的操作系统调用接口,使得线程的状态在这五个状态之间来回切换。几种状态间的转换关系如下图所示:

在这里插入图片描述

  • 线程通过调用函数 rt_thread_create/init() 进入到初始状态(RT_THREAD_INIT)
  • 初始状态的线程通过调用函数 rt_thread_startup() 进入到就绪状态(RT_THREAD_READY);就绪状态的线程被调度器调度后进入运行状态(RT_THREAD_RUNNING)
  • 当处于运行状态的线程调用 rt_thread_delay(),rt_sem_take(),rt_mutex_take(),rt_mb_recv() 等函数或者获取不到资源时,将进入到挂起状态(RT_THREAD_SUSPEND)
  • 处于挂起状态的线程,如果等待超时依然未能获得资源或由于其他线程释放了资源,那么它将返回到就绪状态。
  • 挂起状态的线程,如果调用 rt_thread_delete/detach() 函数,将更改为关闭状态(RT_THREAD_CLOSE)
  • 而运行状态的线程,如果运行结束,就会在线程的最后部分执行 rt_thread_exit() 函数,将状态更改为关闭状态
重要概念3 线程优先级

RT-Thread 线程的优先级是表示线程被调度的优先程度。每个线程都具有优先级,线程越重要,赋予的优先级就应越高,线程被调度的可能才会越大。

**RT-Thread 最大支持 256 个线程优先级 (0~255),数值越小的优先级越高,0 为最高优先级。**在一些资源比较紧张的系统中,可以根据实际情况选择只支持 8 个或 32 个优先级的系统配置;对于 ARM Cortex-M 系列,普遍采用 32 个优先级。最低优先级默认分配给空闲线程使用,用户一般不使用。在系统中,当有比当前线程优先级更高的线程就绪时,当前线程将立刻被换出,高优先级线程抢占处理器运行。

绝大多数的RTOS的优先级都是数值越小,优先级越高,除了FreeRTOS。

在这里插入图片描述

重要概念4 系统线程:空闲线程 主线程
空闲线程

**空闲线程(idle)是系统创建的最低优先级的线程,线程状态永远为就绪态。当系统中无其他就绪线程存在时,调度器将调度到空闲线程,它通常是一个死循环,且永远不能被挂起。**另外,空闲线程在 RT-Thread 也有着它的特殊用途:

若某线程运行完毕,系统将自动删除线程:自动执行 rt_thread_exit() 函数,先将该线程从系统就绪队列中删除,再将该线程的状态更改为关闭状态,不再参与系统调度,然后挂入 rt_thread_defunct 僵尸队列(资源未回收、处于关闭状态的线程队列)中,最后空闲线程会回收被删除线程的资源。

空闲线程也提供了接口来运行用户设置的钩子函数,在空闲线程运行时会调用该钩子函数,适合处理功耗管理、看门狗喂狗等工作。空闲线程必须有得到执行的机会,即其他线程不允许一直while(1)死卡,必须调用具有阻塞性质的函数;否则例如线程删除、回收等操作将无法得到正确执行。

主线程

在系统启动时,系统会创建 main 线程,它的入口函数为 main_thread_entry(),用户的应用入口函数 main() 就是从这里真正开始的,系统调度器启动后,main 线程就开始运行,过程如下图,用户可以在 main() 函数里添加自己的应用程序初始化代码。

在这里插入图片描述

线程管理函数API
/*
 * thread interface
 */
rt_err_t rt_thread_init(struct rt_thread *thread,
                        const char       *name,
                        void (*entry)(void *parameter),
                        void             *parameter,
                        void             *stack_start,
                        rt_uint32_t       stack_size,
                        rt_uint8_t        priority,
                        rt_uint32_t       tick);
rt_err_t rt_thread_detach(rt_thread_t thread);
rt_thread_t rt_thread_create(const char *name,
                             void (*entry)(void *parameter),
                             void       *parameter,
                             rt_uint32_t stack_size,
                             rt_uint8_t  priority,
                             rt_uint32_t tick);
rt_thread_t rt_thread_self(void);
rt_thread_t rt_thread_find(char *name);
rt_err_t rt_thread_startup(rt_thread_t thread);
rt_err_t rt_thread_delete(rt_thread_t thread);

rt_err_t rt_thread_yield(void);
rt_err_t rt_thread_delay(rt_tick_t tick);
rt_err_t rt_thread_mdelay(rt_int32_t ms);
rt_err_t rt_thread_control(rt_thread_t thread, int cmd, void *arg);
rt_err_t rt_thread_suspend(rt_thread_t thread);
rt_err_t rt_thread_resume(rt_thread_t thread);
void rt_thread_timeout(void *parameter);


#ifdef RT_USING_SIGNALS //使用信号量
void rt_thread_alloc_sig(rt_thread_t tid);
void rt_thread_free_sig(rt_thread_t tid);
int  rt_thread_kill(rt_thread_t tid, int sig);
#endif

#ifdef RT_USING_HOOK  //使用钩子函数
void rt_thread_suspend_sethook(void (*hook)(rt_thread_t thread));
void rt_thread_resume_sethook (void (*hook)(rt_thread_t thread));
void rt_thread_inited_sethook (void (*hook)(rt_thread_t thread));
#endif

/*
 * idle thread interface
 */
void rt_thread_idle_init(void);
#if defined(RT_USING_HOOK) || defined(RT_USING_IDLE_HOOK)
rt_err_t rt_thread_idle_sethook(void (*hook)(void));
rt_err_t rt_thread_idle_delhook(void (*hook)(void));
#endif
void rt_thread_idle_excute(void);
rt_thread_t rt_thread_idle_gethandler(void);

在这里插入图片描述

线程的创建(动态创建、静态创建)

动态创建 rt_thread_create

rt_thread_t rt_thread_create(const char* name,
                            void (*entry)(void* parameter),
                            void* parameter,
                            rt_uint32_t stack_size,
                            rt_uint8_t priority,
                            rt_uint32_t tick);
参数描述
name线程的名称;线程名称的最大长度由 rtconfig.h 中的宏 RT_NAME_MAX 指定,多余部分会被自动截掉
entry线程入口函数
parameter线程入口函数参数
stack_size线程栈大小,单位是字节
priority线程的优先级。优先级范围根据系统配置情况(rtconfig.h 中的 RT_THREAD_PRIORITY_MAX 宏定义),如果支持的是 256 级优先级,那么范围是从 0~255,数值越小优先级越高,0 代表最高优先级
tick线程的时间片大小。时间片(tick)的单位是操作系统的时钟节拍。当系统中存在相同优先级线程时,这个参数指定线程一次调度能够运行的最大时间长度。这个时间片运行结束时,调度器自动选择下一个就绪态的同优先级线程进行运行
返回值
thread线程创建成功,返回线程句柄
RT_NULL线程创建失败

一般情况下,入口参数没有的情况比较多。

静态创建 rt_thread_init

rt_err_t rt_thread_init(struct rt_thread *thread,
                        const char       *name,
                        void (*entry)(void *parameter),
                        void             *parameter,
                        void             *stack_start,
                        rt_uint32_t       stack_size,
                        rt_uint8_t        priority,
                        rt_uint32_t       tick);
参数描述
thread线程句柄。线程句柄由用户提供出来,并指向对应的线程控制块内存地址
name线程的名称;线程名称的最大长度由 rtconfig.h 中定义的 RT_NAME_MAX 宏指定,多余部分会被自动截掉
entry线程入口函数
parameter线程入口函数参数
stack_start线程栈起始地址
stack_size**线程栈大小,单位是字节。**在大多数系统中需要做栈空间地址对齐(例如 ARM 体系结构中需要向 4 字节地址对齐)
priority线程的优先级。优先级范围根据系统配置情况(rtconfig.h 中的 RT_THREAD_PRIORITY_MAX 宏定义),如果支持的是 256 级优先级,那么范围是从 0 ~ 255,数值越小优先级越高,0 代表最高优先级
tick**线程的时间片大小。**时间片(tick)的单位是操作系统的时钟节拍。当系统中存在相同优先级线程时,这个参数指定线程一次调度能够运行的最大时间长度。这个时间片运行结束时,调度器自动选择下一个就绪态的同优先级线程进行运行
返回
RT_EOK线程创建成功
RT_ERROR线程创建失败

在静态创建过程中,需要预先定义一个数组,提前申请空间,动态创建过程则不需要,系统会从动态堆内存中分配一个线程句柄以及按照参数中指定的栈大小从动态堆内存中分配相应的空间。

线程启动

创建(初始化)的线程状态处于初始状态,并未进入就绪线程的调度队列,我们可以在线程初始化 / 创建成功后调用下面的函数接口让该线程进入就绪态:

rt_err_t rt_thread_startup(rt_thread_t thread);

当调用这个函数时,将把线程的状态更改为就绪状态,并放到相应优先级队列中等待调度。如果新启动的线程优先级比当前线程优先级高,将立刻切换到这个线程。线程启动接口 rt_thread_startup() 的参数和返回值见下表:

参数描述
thread线程句柄
返回
RT_EOK线程启动成功
RT_ERROR线程启动失败
/*
 * 程序清单:创建、初始化/脱离线程
 *
 * 这个例子会创建两个线程,一个动态线程,一个静态线程。
 * 静态线程在运行完毕后自动被系统脱离,动态线程打印计数。
 */
#include <rtthread.h>

#define THREAD_PRIORITY         15
#define THREAD_STACK_SIZE       512
#define THREAD_TIMESLICE        5

static rt_thread_t tid1 = RT_NULL;

/* 线程1的入口函数 */
static void thread1_entry(void *parameter)
{
    rt_uint32_t count = 0;

    for (count =0 ;count< 20;count++)
    {
        /* 线程1采用低优先级运行,打印计数值20结束 */
        rt_kprintf("thread1 count: %d\n", count ++);
    }
    rt_kprintf("thread1 exit\n");
}

ALIGN(RT_ALIGN_SIZE)
static char thread2_stack[1024];
static struct rt_thread thread2;

/* 线程2入口 */
static void thread2_entry(void *param)
{
    rt_uint32_t count = 0;

    /* 线程2拥有较高的优先级,以抢占线程1而获得执行 */
    for (count = 0; count < 10 ; count++)
    {
        /* 线程2打印计数值 */
        rt_kprintf("thread2 count: %d\n", count);
    }
    rt_kprintf("thread2 exit\n");

    /* 线程2运行结束后也将自动被系统脱离 */
}

/* 线程示例 */
int thread_sample(void)
{
    /* 创建线程1,名称是thread1,入口是thread1_entry*/
    tid1 = rt_thread_create("thread1",
                            thread1_entry, RT_NULL,
                            THREAD_STACK_SIZE,
                            THREAD_PRIORITY, THREAD_TIMESLICE);

    /* 如果获得线程控制块,启动这个线程 */
    if (tid1 != RT_NULL)
        rt_thread_startup(tid1);

    /* 初始化线程2,名称是thread2,入口是thread2_entry */
    rt_thread_init(&thread2,
                   "thread2",
                   thread2_entry,
                   RT_NULL,
                   &thread2_stack[0],
                   sizeof(thread2_stack),
                   THREAD_PRIORITY - 1, THREAD_TIMESLICE);
    rt_thread_startup(&thread2);

    return 0;
}

/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(thread_sample, thread sample);

#include "main.h"

void Hardware_Init(void)
{
    SystemInit (); 
   	systick_config();
    bsp_uart_init();
    HW_LED_Init();  
    
}

int main(void)
{
	Hardware_Init();
    printf("SystemInit [ok] \r\n");
    printf("systick_config[ok] \r\n");
    printf("bsp_uart_init [ok] \r\n");
    printf("Hardware_Init [ok] \r\n");
    printf("LED_Init [ok] \r\n");
    printf("GD32307E-START Board Testing \r\n");
    
    
    printf("GD32307E-START Board thread_sample test start...\r\n");
    thread_sample();
    printf("GD32307E-START Board thread_sample test end...\r\n");
    
    while(1)
    {
        gpio_bit_set(GPIOC,GPIO_PIN_6);
        rt_thread_delay(500);   /* 延时500个tick */
        rt_kprintf("led_thread running,LED1_ON\r\n");
        
        gpio_bit_reset(GPIOC,GPIO_PIN_6);    
        rt_thread_delay(500);   /* 延时500个tick */		 		
        rt_kprintf("led_thread running,LED1_OFF\r\n");
    }  
}

在这里插入图片描述
整个程序先执行thread2,因为THREAD_PRIORITY优先级比thread1高,thread2打印完成10次计数值之后,就执行thread1,打印完成20次计数值结束。

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

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

相关文章

Java JVM类加载机制原理剖析

目录 前言一、什么是类加载二、类加载子系统三、类的加载过程2.1、加载2.2、验证2.3、准备2.4、解析2.5、初始化 四、类加载器(ClassLoader) 前言 Java类要加载到JVM中的&#xff0c;会经过一系列的加载过程&#xff0c;这个过程就是在类加载子系统中实现的&#xff0c;当我们用…

函数栈帧的创建和销毁

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言 1. 什么是函数栈帧 2. 理解函数栈帧能解决什么问题呢&#xff1f; 3. 函数栈帧的创建和销毁解析 3.1 什么是栈&#xff1f; 3.2 认识相关寄存器和汇编指令 3.3…

【深度学习】注意力机制(一)

本文介绍一些注意力机制的实现&#xff0c;包括SE/ECA/GE/A2-Net/GC/CBAM。 目录 一、SE&#xff08;Squeeze-and-Excitation&#xff09; 二、ECA&#xff08;Efficient Channel Attention&#xff09; 三、GE&#xff08;Gather-Excite&#xff09; 四、A2-Net(Double A…

USB基础知识点介绍

本文主要介绍USB2.0相关的知识点。 USB 2.0介绍 USB 2.0是一种通用串行总线&#xff08;Universal Serial Bus&#xff09;的接口标准&#xff0c;是USB&#xff08;Universal Serial Bus&#xff09;技术的第二代版本。它于2000年4月发布&#xff0c;是USB 1.1的升级版本。 …

交易历史记录20231208 记录

昨日回顾&#xff1a; SELECT TOP 10000 CODE,成交额排名,净流入排名,代码,名称,DDE大单金额,涨幅,所属行业,主力净额,DDE大单净量,CONVERT(DATETIME, 最后涨停时间, 120) AS 最后涨停时间 FROM dbo.全部&#xff21;股20231208_ALL WHERE 连板天 > 1AND DDE大单净量 > …

通信:mqtt学习网址

看这个网址&#xff1a;讲的很详细&#xff0c;后面补实战例子 第一章 - MQTT介绍 MQTT协议中文版 (gitbooks.io)https://mcxiaoke.gitbooks.io/mqtt-cn/content/mqtt/01-Introduction.html

3.DevEco Studio安装鸿蒙手机app本地模拟器

配合Intel CPU启动模拟器 解决措施 打开任务管理器&#xff0c;在“性能”选项&#xff0c;检查CPU虚拟化是否已经启用。如果未启用&#xff0c;需要进入电脑的BIOS中&#xff0c;将CPU的“Intel Virtualization Technology”选项开启。 点击New Emulator 文档中心 解决措施…

python+pytest接口自动化(12)-自动化用例编写思路 (使用pytest编写一个测试脚本)

经过之前的学习铺垫&#xff0c;我们尝试着利用pytest框架编写一条接口自动化测试用例&#xff0c;来厘清接口自动化用例编写的思路。 我们在百度搜索天气查询&#xff0c;会出现如下图所示结果&#xff1a; 接下来&#xff0c;我们以该天气查询接口为例&#xff0c;编写接口测…

Android Studio连接MYSQL数据库

首先导入mysql的jar包&#xff0c;这里连接的是8版本的。 这里之前到如果mysql的jar包了 首先跳到Project模式&#xff1a; 直接复制粘贴到这里&#xff1a; 这里之前到如果了。想删掉重新导入一次&#xff0c;但是报错,什么ioexception。这里将Project Structure中的Moudle中的…

【每日一题】—— D. Divide and Equalize(Codeforces Round 903 (Div. 3))(数学、数论)

&#x1f30f;博客主页&#xff1a;PH_modest的博客主页 &#x1f6a9;当前专栏&#xff1a;每日一题 &#x1f48c;其他专栏&#xff1a; &#x1f534; 每日反刍 &#x1f7e1; C跬步积累 &#x1f7e2; C语言跬步积累 &#x1f308;座右铭&#xff1a;广积粮&#xff0c;缓称…

【分治】最接近点对Python实现

文章目录 [toc]问题描述一维最接近点对算法Python实现 二维最接近点对算法分治算法时间复杂性Python实现 问题描述 给定平面上 n n n个点&#xff0c;找其中的一对点&#xff0c;使得在 n n n个点组成的所有点对中&#xff0c;该点对的距离最小 一维最接近点对算法 Python实…

探索无监督域自适应,释放语言模型的力量:基于检索增强的情境学习实现知识迁移...

深度学习自然语言处理 原创作者: Xnhyacinth 在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;如何有效地进行无监督域自适应(Unsupervised Domain Adaptation, UDA) 一直是研究的热点和挑战。无监督域自适应的目标是在目标域无标签的情况下&#xff0c;将源域的知识…

docker安装elasticsearch和kibana

docker系列 1、CentOS7安装docker 2、docker安装rabbitmq 3、docker安装mysql docker安装elasticsearch和kibana docker系列一、安装elasticsearch二、安装kibana三、安装ik分词器1、分词器说明2、安装分词器 本篇文章所采用的elasticsearch和kibana版本以及ik分词器都是7.12.…

AS安装目录

编辑器&#xff1a; sdk: gradle: gradle使用的jdk目录&#xff1a;Gradle使用的jdk是android studio安装目录下的jbr 成功项目的android studio配置&#xff1a;

动态内存的管理malloc、free、calloc、realloc

身在井隅&#xff0c;心向星光 眼里有诗&#xff0c;自在远方 目录 动态内存的简单介绍 动态内存的优势 可以控制内存的大小 可以多次利用这部分空间 动态内存函数malloc、free malloc开辟函数 free释放函数 动态内存函数calloc、realloc calloc开辟函数 realloc调整函数 动…

生产问题: 利用线程Thread预加载数据缓存,其它类全局变量获取缓存偶发加载不到

生产问题: 利用线程Thread预加载数据缓存偶发加载不到 先上代码 public class ThreadTest {//本地缓存Map<String, Object> map new HashMap<String, Object>();class ThreadA implements Runnable{Overridepublic void run() {System.out.println("Thread…

笔记本电脑word打字延迟特别大,但是浏览器中打字没有延迟,如何解决这个问题。

问题描述&#xff1a; 笔记本电脑word打字延迟特别大&#xff0c;但是浏览器中打字没有延迟&#xff0c;如何解决这个问题。&#xff08;之前以为是自己的电脑用了6年&#xff0c;用的时间久了&#xff0c;硬件老化导致的&#xff0c;本来想直接换电脑的&#xff0c;但是想着去…

鸿蒙前端开发-构建第一个ArkTS应用(Stage模型)

创建ArkTS工程 若首次打开DevEco Studio&#xff0c;请点击Create Project创建工程。如果已经打开了一个工程&#xff0c;请在菜单栏选择File > New > Create Project来创建一个新工程。 选择Application应用开发&#xff08;本文以应用开发为例&#xff0c;Atomic Serv…

数据库连接池Druid

在 Spring Boot 项目中&#xff0c;数据库连接池已经成为标配&#xff0c;然而&#xff0c;我曾经遇到过不少连接池异常导致业务错误的事故。很多经验丰富的工程师也可能不小心在这方面出现问题。 在这篇文章中&#xff0c;我们将探讨数据库连接池&#xff0c;深入解析其实现机…

探索开源游戏的乐趣与无限可能 | 开源专题 No.47

CleverRaven/Cataclysm-DDA Stars: 9.0k License: NOASSERTION Cataclysm&#xff1a;Dark Days Ahead 是一个回合制的生存游戏&#xff0c;设定在一个后启示录世界中。尽管有些人将其描述为 “僵尸游戏”&#xff0c;但 Cataclysm 远不止于此。在这个残酷、持久、程序生成的世…