当前位置: 首页 > news >正文

【MTI 6.S081 Lab】thread

【MTI 6.S081 Lab】thread

  • 前言
  • 调度
  • Uthread: switching between threads (moderate)
    • 实验任务
    • Hints
    • 解决方案
      • thread_switch
      • thread_create()
      • thread_schedule()
  • Using threads (moderate)
    • 实验任务
    • 解决方案
  • Barrier (moderate)
    • 实验任务
    • 解决方案

本实验前去看《操作系统导论》第29章基于锁的并发数据结构,将会是很有帮助的。

前言

在做MIT 6.S081的实验过程中,每看一些讲座(lecture)和xv6 book就来做相应的实验。这个实验是让我最爽的两个实验之一(还有一个是trap),因为其他大多c语言,在我已经有了很多关于操作系统的知识的情况下,实现并没有那么的让人惊艳,但是这个实验确实让人非常惊艳。

这个实验里面的进程切换、线程切换,以前我确实知道怎么做的,但是像这样通过改变寄存器的值,以前我是没有想到的,所以以前只知道要保存状态,但是对于如何保存不知道原理,这次实验的过程中,通过理解源码,懂了这个,很神奇的东西。

调度

实现多路复用(multiplexing)的挑战:

  1. 如何从一个进程切换到另一个进程?尽管上下文切换的思想很简单,但它的实现是xv6中最不透明的代码之一
  2. 如何以对用户进程透明的方式强制切换?xv6使用标准技术,通过定时器终端驱动上下文切换
  3. 许多CPU可能同时在进程之间切换,使用一个锁方案来避免争用是很有必要的
  4. 进程退出时必须释放进程的内存以及其他资源,但它不能自己完成所有这一切,因为它不能在仍然使用自己内核栈的情况下释放它
  5. 多核机器的每个核心都必须记住它正在执行哪个进程,以便系统调用正确影响对应进程的内核状态
  6. sleep运行一个进程放弃CPU,wakeup允许另一个进程唤醒第一个进程。需要小心避免唤醒通知丢失的竞争。

Uthread: switching between threads (moderate)

在本练习中,您将为用户级线程系统设计上下文切换机制,然后实现它。首先,您的xv6有两个文件user/uthread.c和user/uthread_switch.S,并且Makefile中有一个规则来构建uthread程序。uthread.c包含大部分用户级线程包,以及三个简单测试线程的代码。线程程序包缺少一些用于创建线程和在线程之间切换的代码。

实验任务

您的工作是制定一个创建线程和保存/恢复寄存器以在线程之间切换的计划,并实现该计划。完成后,make grade应该表明您的解决方案通过了uthread测试。

完成后,当您在xv6上运行uthread时,您应该会看到以下输出(这三个线程可能以不同的顺序启动):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KHSla5rT-1690632901891)(Images/Screenshot from 2023-07-27 18-43-03.png)]

这个输出来自三个测试线程,每个线程都有一个循环,打印一行,然后将CPU分配给其他线程。

然而,现在你将看不到输出,因为咩有上下文切换代码。

你将需要在user/uthread.c中添加代码thread_create()thread_schedule(),在user/uthread_switch.S中添加thread_switch。一个目标是确保当thread_schedule()第一次运行给定线程时,该线程在自己的堆栈上执行传递给thread_create()的函数。另一个目标是确保thread_switch保存要切换的线程的寄存器,恢复要切换到的线程的注册表,并返回到后一个线程指令中上次停止的点。您必须决定保存/恢复寄存器的位置;修改struct thread以容纳寄存器是一个不错的计划。您需要在thread_schedule中添加对thread_switch的调用;您可以向threadswitch传递所需的任何参数,但目的是从threadt切换到nextthread。

Hints

  • thread_switch仅仅需要保存被调用者保存寄存器吗,为什么?

    :是,其他调用者保存寄存器已经在调用thread_swtich时保存在当前线程的栈上。

  • 你能在user/uthread.asm中看到uthread的汇编代码,可能更方便调试

  • 为了测试你的代码,使用gdb单步调试thread_switch将是有用的,你可以通过这种方式开始:

    (gdb) file user/_uthread
    Reading symbols from user/_uthread...
    (gdb) b uthread.c:60
    

    这将在uthread.c的第60行设置断点。断点可能(也可能不)在您运行uthread之前被触发。这怎么可能发生?

    一旦xv6shell运行,键入“uthread”,gdb将在第60行中断。如果您从另一个进程中遇到了断点,请继续进行,直到您在uthread进程中遇到断点为止。现在,您可以键入以下命令来检查uthread的状态:

    (gdb) p/x *next_thread
    

    使用“x”,您可以检查内存位置的内容:

    (gdb) x/x next_thread->stack
    

    您可以跳到thread_switch的开头,这样:

    (gdb) b thread_switch
    (gdb) c
    

    单步执行汇编代码

    (gdb) si
    

    gdb的在线文档 is here.

解决方案

thread_switch

  .text/** save the old thread's registers,* restore the new thread's registers.*/.globl thread_switch
thread_switch:/* YOUR CODE HERE */sd ra, 0(a0)sd sp, 8(a0)sd s0, 16(a0)sd s1, 24(a0)sd s2, 32(a0)sd s3, 40(a0)sd s4, 48(a0)sd s5, 56(a0)sd s6, 64(a0)sd s7, 72(a0)sd s8, 80(a0)sd s9, 88(a0)sd s10, 96(a0)sd s11, 104(a0)ld ra, 0(a1)ld sp, 8(a1)ld s0, 16(a1)ld s1, 24(a1)ld s2, 32(a1)ld s3, 40(a1)ld s4, 48(a1)ld s5, 56(a1)ld s6, 64(a1)ld s7, 72(a1)ld s8, 80(a1)ld s9, 88(a1)ld s10, 96(a1)ld s11, 104(a1)ret    /* return to ra */

thread_create()

void 
thread_create(void (*func)())
{struct thread *t;for (t = all_thread; t < all_thread + MAX_THREAD; t++) {if (t->state == FREE) break;}t->state = RUNNABLE;// YOUR CODE HERE// 设置线程栈,设置返回返回地址,调用线程调度函数t->context.sp = (uint64)(t->stack + STACK_SIZE);t->context.ra = (uint64)func;     // 使得在func处返回
}

thread_schedule()

void 
thread_schedule(void)
{...if (current_thread != next_thread) {         /* switch threads?  */next_thread->state = RUNNING;t = current_thread;current_thread = next_thread;/* YOUR CODE HERE* Invoke thread_switch to switch from t to next_thread:* thread_switch(??, ??);*/// 恢复当前进程的上下文,保存上一个进程t的上下文,此时会自动从上一个切换的上下文位置切换// 这里会破坏a0和a1thread_switch((uint64)&t->context, (uint64)&current_thread->context);} elsenext_thread = 0;
}

Using threads (moderate)

在本次作业中,您将探索使用哈希表使用线程和锁进行并行编程。您应该在具有多个核心的真实Linux或MacOS计算机(不是xv6,不是qemu)上执行此任务。最新的笔记本电脑都有多核处理器。

此作业使用UNIX pthread线程库。您可以通过man phreads从手册页中找到有关它的信息,也可以在网上查看,例如here, here, and here.

文件notxv6/ph.c包含一个简单的哈希表,如果从单个线程使用,该哈希表是正确的,但如果从多个线程使用,则该哈希表不正确。在您的xv6主目录(可能~/xv6-labs-2021)中,键入以下内容:

$ make ph
$ ./ph 1

请注意,要构建ph,Makefile使用操作系统的gcc,而不是6.S081工具。ph的参数指定对哈希表执行put和get操作的线程数。运行一段时间后,ph 1将产生类似的输出:

100000 puts, 3.991 seconds, 25056 puts/second
0: 0 keys missing
100000 gets, 3.981 seconds, 25118 gets/second

你看到的数字可能与这个样本输出相差两倍或更多,这取决于你的电脑速度有多快,它是否有多个内核,以及它是否忙于做其他事情。

ph运行两个基准。首先,它通过调用put()将大量键添加到哈希表中,并以每秒puts为单位打印实现的速率。它使用get()从哈希表中获取密钥。它打印由于put而应该在哈希表中但丢失的数字键(在这种情况下为零),并打印每秒获得的次数。

您可以告诉ph同时从多个线程使用其哈希表,方法是给它一个大于1的参数。尝试ph 2:

$ ./ph 2
100000 puts, 1.885 seconds, 53044 puts/second
1: 16579 keys missing
0: 16579 keys missing
200000 gets, 4.322 seconds, 46274 gets/second

该ph2输出的第一行表明,当两个线程同时向哈希表添加条目时,它们的总插入率为每秒53044次。这大约是运行ph1的单个线程的速率的两倍。这是一个大约2倍的出色“并行加速”,这是人们所希望的(即每单位时间产生两倍功的两倍内核)。

然而,缺少16579个键的两行表示哈希表中不存在大量本应存在的键。也就是说,puts本应将这些键添加到哈希表中,但出现了问题。看看notxv6/ph.c,特别是put()和insert()。

实验任务

  • 为什么2个线程的时会缺少一些key,而1个线程时没有缺少key?识别能在两个线程时导致键缺失的代码片。在answers-thread.txt中提交您的序列并附上简短解释

    :在put和insert函数中都可能发生错误

    static void 
    insert(int key, int value, struct entry **p, struct entry *n)
    {struct entry *e = malloc(sizeof(struct entry));e->key = key;e->value = value;e->next = n;*p = e;
    }static 
    void put(int key, int value)
    {int i = key % NBUCKET;// is the key already present?struct entry *e = 0;for (e = table[i]; e != 0; e = e->next) {if (e->key == key)break;}if(e){// update the existing key.e->value = value;} else {// the new is new.insert(key, value, &table[i], table[i]);}
    }
    

    假设现在有一个线程运行到第7行,此时添加的key为1,在执行第八行前,另一个线程刚好在put中搜索key=1是否存在,当然此时的结果是不存在,所以会被插入,此时key=1被重复插入了导致错误。

    另一个问题是,同样假设当前两个线程均运行到第7行,此时会导致×p的更新只有一个更新有效,导致key缺失。

  • 为了避免这种事件序列,请在notxv6/ph.c中的put和get中插入lock和unlock语句,以便在两个线程中丢失的key数始终为0。相关的pthread调用包括:

    pthread_mutex_t lock;            // declare a lock
    pthread_mutex_init(&lock, NULL); // initialize the lock
    pthread_mutex_lock(&lock);       // acquire lock
    pthread_mutex_unlock(&lock);     // release lock
    

    当make grade表示您的代码通过了ph_safe测试时,您就完成了,该测试需要两个线程的零丢失键。在这一点上,不通过ph_fast测试是可以的(也就是说只要我们自己测试,没有missing就行)。

解决方案

在原始代码单线程情况下,我的机器的输出如下

$ ./ph 1
100000 puts, 10.889 seconds, 9183 puts/second
0: 0 keys missing
100000 gets, 11.494 seconds, 8700 gets/second

在修改后,给每个table一个锁,加在上面展现的代码的地16行和第28行的位置,编译程序,得到的双线程结果是

$ ./ph 2
100000 puts, 5.834 seconds, 17140 puts/second
0: 0 keys missing
1: 0 keys missing
200000 gets, 10.783 seconds, 18547 gets/second

修改代码,使一些put操作在保持正确性的同时并行运行。当make grade表示您的代码通过了ph_safe和ph_fast测试时,您就完成了。ph_fast测试要求两个线程每秒的输出量至少是一个线程的1.25倍。(这里我用的小粒度的锁,基本达到了两倍速度,所以make grade相应测试肯定通过)

Barrier (moderate)

在这项任务中,您将实现一个屏障:应用程序中的一个点,所有参与线程都必须等待,直到所有其他参与线程也到达该点。您将使用pthread条件变量,这是一种类似于xv6的睡眠和唤醒的序列协调技术。

您应该在真实的计算机上(不是xv6,也不是qemu)执行此任务。

文件notxv6/barrier.c包含一个损坏的屏障。

$ make barrier
$ ./barrier 2
barrier: notxv6/barrier.c:42: thread: Assertion `i == t' failed.

2指定在barrier.c中同步的线程数。每个线程执行一个循环。在每次循环迭代中,线程都会调用barrier(),然后休眠随机微秒。assert触发,因为一个线程在另一个线程到达屏障之前就离开了屏障。所需的行为是,每个线程在barrier()中阻塞,直到所有线程都调用了barrier()

实验任务

您的目标是实现所需的障碍行为。除了您在ph赋值中看到的锁基元之外,您还需要以下新的pthread基元;请看这里和这里了解详细信息。

pthread_cond_wait(&cond,&mutex);//在cond上进入睡眠,释放锁互斥,在唤醒时获取
pthread_cond_broadcast(&cond);//唤醒每一个睡在cond上的线程

确保你的解决方案通过make grade的barrier测试。

pthread_cond_wait 被调用的时候释放mutex,在返回时重新获得mutex

我们已经给你barrier_init()。你的任务是执行barrier(),以便于panic不发生。我们已经为你定义struct barrier,他的一些字段对你有用

有两个问题使您的任务复杂化:

  • 你必须处理一系列的障碍调用,每一次我们都会调用一轮。bstate.round记录本轮。每次所有线程到达屏障时,都应该增加bstate.round。
  • 您必须处理这样一种情况,即一个线程在其他线程退出屏障之前围绕循环进行竞争。特别是,从一轮到下一轮都在重新使用bstate.nthread变量。确保在上一轮仍在使用bstate.nthread时,离开障碍并在循环中奔跑的线程不会增加bstate.nhread。

使用一个、两个或两个以上的线程测试代码。

解决方案

对进入barrier的线程数计数,一旦最后一个线程到达了,那么不要再

static void 
barrier()
{// YOUR CODE HERE//// Block until all threads have called barrier() and// then increment bstate.round.//pthread_mutex_lock(&bstate.barrier_mutex);    // 进入临界区bstate.nthread++;if (bstate.nthread != nthread) {pthread_cond_wait(&bstate.barrier_cond, &bstate.barrier_mutex);   // 在这里等待其他的进入} else {    // 所有的线程都到这里了bstate.nthread = 0;bstate.round++;// 一定要在round、nthread更新后才能唤醒,因为如果先唤醒,此时别的线程执行,到t=bstate.round,但是没有更新,导致t!=i,会assertpthread_cond_broadcast(&bstate.barrier_cond);}pthread_mutex_unlock(&bstate.barrier_mutex); 
}

相关文章:

【MTI 6.S081 Lab】thread

【MTI 6.S081 Lab】thread 前言调度Uthread: switching between threads (moderate)实验任务Hints解决方案thread_switchthread_create()thread_schedule() Using threads (moderate)实验任务解决方案 Barrier (moderate)实验任务解决方案 本实验前去看《操作系统导论》第29章基…...

AWS / VPC 云流量监控

由于安全性、数据现代化、增长、灵活性和成本等原因促使更多企业迁移到云&#xff0c;将数据存储在本地的组织正在使用云来存储其重要数据。亚马逊网络服务&#xff08;AWS&#xff09;仍然是最受追捧和需求的服务之一&#xff0c;而亚马逊虚拟私有云&#xff08;VPC&#xff0…...

【C++学习笔记】extern “c“以及如何查看符号表

如何查看符号表 要查看.a文件的内容&#xff0c;可以使用ar命令。下面是一些常见的用法&#xff1a; 列出.a文件中包含的所有文件&#xff1a; ar t <filename.a>提取.a文件中的单个文件&#xff1a; ar x <filename.a> <filename.o>将.a文件中的所有文件提…...

24考研数据结构-数组和特殊矩阵

目录 数据结构&#xff1a;数组与特殊矩阵数组数组的特点数组的用途 特殊矩阵对角矩阵上三角矩阵和下三角矩阵稀疏矩阵特殊矩阵的用途 结论 3.4 数组和特殊矩阵3.4.1数组的存储结构3.4.2普通矩阵的存储3.4.3特殊矩阵的存储1. 对称矩阵(方阵)2. 三角矩阵(方阵)3. 三对角矩阵(方阵…...

服务器后台运行程序

代码运行 要让代码在服务器后台运行&#xff0c;有多种方法。在 Linux 系统中&#xff0c;最常见的有以下几种方式&#xff1a; **1. 使用 & 符号&#xff1a;** 在命令后面添加 & 符号可以让程序在后台运行。例如&#xff1a; bash python myscript.py &但是…...

大数据课程D7——hadoop的YARN

文章作者邮箱&#xff1a;yugongshiyesina.cn 地址&#xff1a;广东惠州 ▲ 本章节目的 ⚪ 了解YARN的概念和结构&#xff1b; ⚪ 掌握YARN的资源调度流程&#xff1b; ⚪ 了解Hadoop支持的资源调度器&#xff1a;FIFO、Capacity、Fair&#xff1b; ⚪ 掌握YA…...

Rust vs Go:常用语法对比(十三)

题图来自 Go vs. Rust: The Ultimate Performance Battle 241. Yield priority to other threads Explicitly decrease the priority of the current process, so that other execution threads have a better chance to execute now. Then resume normal execution and call f…...

【【51单片机DA转换模块】】

爆改直流电机&#xff0c;DA转换器 main.c #include <REGX52.H> #include "Delay.h" #include "Timer0.h"sbit DAP2^1;unsigned char Counter,Compare; //计数值和比较值&#xff0c;用于输出PWM unsigned char i;void main() {Timer0_Init();whil…...

[SQL挖掘机] - 字符串函数 - substring

介绍: substring函数是在mysql中用于提取字符串的一种函数。它接受一个字符串作为输入&#xff0c;并返回从该字符串中指定位置开始的一部分子串。substring函数可以用于获取字符串中的特定字符或子串&#xff0c;以便进行进一步的处理或分析。 用法: 下面是substring函数的…...

第一百一十六天学习记录:C++提高:STL-string(黑马教学视频)

string基本概念 string是C风格的字符串&#xff0c;而string本质上是一个类 string和char区别 1、char是一个指针 2、string是一个类&#xff0c;类内部封装了char*&#xff0c;管理这个字符串&#xff0c;是一个char型的容器。 特点&#xff1a; string类内部封装了很多成员方…...

Meta-Transformer 多模态学习的统一框架

Meta-Transformer是一个用于多模态学习的新框架&#xff0c;用来处理和关联来自多种模态的信息&#xff0c;如自然语言、图像、点云、音频、视频、时间序列和表格数据&#xff0c;虽然各种数据之间存在固有的差距&#xff0c;但是Meta-Transformer利用冻结编码器从共享标记空间…...

tinkerCAD案例:24.Tinkercad 中的自定义字体

tinkerCAD案例&#xff1a;24.Tinkercad 中的自定义字体 原文 Tinkercad Projects Tinkercad has a fun shape in the Shape Generators section that allows you to upload your own font in SVG format and use it in your designs. I’ve used it for a variety of desi…...

list与流迭代器stream_iterator

运行代码&#xff1a; //list与流迭代器 #include"std_lib_facilities.h" //声明Item类 struct Item {string name;int iid;double value;Item():name(" "),iid(0),value(0.0){}Item(string ss,int ii,double vv):name(ss),iid(ii),value(vv){}friend ist…...

九耶:冯·诺伊曼体系

冯诺伊曼体系&#xff08;Von Neumann architecture&#xff09;是一种计算机体系结构&#xff0c;它由匈牙利数学家冯诺伊曼于1945年提出。冯诺伊曼体系是现代计算机体系结构的基础&#xff0c;几乎所有的通用计算机都采用了这种体系结构。 冯诺伊曼体系的核心思想是将计算机硬…...

探索UCI心脏病数据:利用R语言和h2o深度学习构建预测模型

一、引言 随着机器学习模型在实际应用中的广泛应用&#xff0c;人们对于模型的解释性和可理解性日益关注。可解释性机器学习是指能够清晰、透明地解释机器学习模型决策过程的一种方法和技术。在许多领域中&#xff0c;如医疗诊断、金融风险评估和自动驾驶等&#xff0c;解释模型…...

基于 moleculer 微服务架构的智能低代码PaaS 平台源码 可视化开发

低代码开发平台源码 低代码管理系统PaaS 平台 无需代码或通过少量代码就可以快速生成应用程序的开发平台。 本套低代码管理后台可以支持多种企业应用场景&#xff0c;包括但不限于CRM、ERP、OA、BI、IoT、大数据等。无论是传统企业还是新兴企业&#xff0c;都可以使用管理后台…...

xrdp登录显示白屏且红色叉

如上图所示&#xff0c;xrdp登录出现了红色叉加白屏&#xff0c;这是因为不正常关闭导致&#xff0c;解决方法其实挺简单的 #进入/usr/tmp cd /usr/tmp #删除对应用户的kdecache-** 文件&#xff08;我这里使用的是kde桌面&#xff09;&#xff0c;例如删除ywj用户对应的文件 …...

Docker安装 Mysql 8.x 版本

文章目录 Docker安装 Mysql 8.0.22Mysql 创建账号并授权Mysql 数据迁移同版本数据迁移跨版本数据迁移 Mysql 5.x 版本与 Mysql 8.x版本是两个大版本&#xff0c;这里演示安装Mysql 8.x版本 Docker安装 Mysql 8.0.22 # 下载mysql $ docker pull mysql 默认安装最新…...

【数理知识】刚体 rigid body 及刚体的运动

文章目录 1 刚体2 刚体一般运动1 平移运动2 旋转运动 Ref 1 刚体 刚体是指在运动中和受力作用后&#xff0c;形状和大小不变&#xff0c;而且内部各点的相对位置不变的物体。绝对刚体实际上是不存在的&#xff0c;只是一种理想模型&#xff0c;因为任何物体在受力作用后&#…...

【UE5 多人联机教程】03-创建游戏

效果 步骤 打开“UMG_MainMenu”&#xff0c;增加创建房间按钮的点击事件 添加如下节点 其中&#xff0c;“FUNL Fast Create Widget”是插件自带的函数节点&#xff0c;内容如下&#xff1a; “创建会话”节点指游戏成功创建一个会话后&#xff0c;游戏的其他实例即可发现&am…...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试

作者&#xff1a;Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位&#xff1a;中南大学地球科学与信息物理学院论文标题&#xff1a;BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接&#xff1a;https://arxiv.…...

HTML 列表、表格、表单

1 列表标签 作用&#xff1a;布局内容排列整齐的区域 列表分类&#xff1a;无序列表、有序列表、定义列表。 例如&#xff1a; 1.1 无序列表 标签&#xff1a;ul 嵌套 li&#xff0c;ul是无序列表&#xff0c;li是列表条目。 注意事项&#xff1a; ul 标签里面只能包裹 li…...

dedecms 织梦自定义表单留言增加ajax验证码功能

增加ajax功能模块&#xff0c;用户不点击提交按钮&#xff0c;只要输入框失去焦点&#xff0c;就会提前提示验证码是否正确。 一&#xff0c;模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍

文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结&#xff1a; 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析&#xff1a; 实际业务去理解体会统一注…...

vue3+vite项目中使用.env文件环境变量方法

vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量&#xff0c;这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...

SQL慢可能是触发了ring buffer

简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

4. TypeScript 类型推断与类型组合

一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式&#xff0c;自动确定它们的类型。 这一特性减少了显式类型注解的需要&#xff0c;在保持类型安全的同时简化了代码。通过分析上下文和初始值&#xff0c;TypeSc…...

PostgreSQL——环境搭建

一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在&#xff0…...

在 Spring Boot 项目里,MYSQL中json类型字段使用

前言&#xff1a; 因为程序特殊需求导致&#xff0c;需要mysql数据库存储json类型数据&#xff0c;因此记录一下使用流程 1.java实体中新增字段 private List<User> users 2.增加mybatis-plus注解 TableField(typeHandler FastjsonTypeHandler.class) private Lis…...