【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)的挑战:
- 如何从一个进程切换到另一个进程?尽管上下文切换的思想很简单,但它的实现是xv6中最不透明的代码之一
- 如何以对用户进程透明的方式强制切换?xv6使用标准技术,通过定时器终端驱动上下文切换
- 许多CPU可能同时在进程之间切换,使用一个锁方案来避免争用是很有必要的
- 进程退出时必须释放进程的内存以及其他资源,但它不能自己完成所有这一切,因为它不能在仍然使用自己内核栈的情况下释放它
- 多核机器的每个核心都必须记住它正在执行哪个进程,以便系统调用正确影响对应进程的内核状态
- 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)¤t_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);
}
相关文章:
![](https://www.ngui.cc/images/no-images.jpg)
【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章基…...
![](https://img-blog.csdnimg.cn/1db6e61fa1dd4e8b97427fab9ea44a5c.png#pic_center)
AWS / VPC 云流量监控
由于安全性、数据现代化、增长、灵活性和成本等原因促使更多企业迁移到云,将数据存储在本地的组织正在使用云来存储其重要数据。亚马逊网络服务(AWS)仍然是最受追捧和需求的服务之一,而亚马逊虚拟私有云(VPC࿰…...
![](https://www.ngui.cc/images/no-images.jpg)
【C++学习笔记】extern “c“以及如何查看符号表
如何查看符号表 要查看.a文件的内容,可以使用ar命令。下面是一些常见的用法: 列出.a文件中包含的所有文件: ar t <filename.a>提取.a文件中的单个文件: ar x <filename.a> <filename.o>将.a文件中的所有文件提…...
![](https://img-blog.csdnimg.cn/fa2633274a3d44fb835bf4e500af3fd4.png)
24考研数据结构-数组和特殊矩阵
目录 数据结构:数组与特殊矩阵数组数组的特点数组的用途 特殊矩阵对角矩阵上三角矩阵和下三角矩阵稀疏矩阵特殊矩阵的用途 结论 3.4 数组和特殊矩阵3.4.1数组的存储结构3.4.2普通矩阵的存储3.4.3特殊矩阵的存储1. 对称矩阵(方阵)2. 三角矩阵(方阵)3. 三对角矩阵(方阵…...
![](https://www.ngui.cc/images/no-images.jpg)
服务器后台运行程序
代码运行 要让代码在服务器后台运行,有多种方法。在 Linux 系统中,最常见的有以下几种方式: **1. 使用 & 符号:** 在命令后面添加 & 符号可以让程序在后台运行。例如: bash python myscript.py &但是…...
![](https://img-blog.csdnimg.cn/c2795c9c59284da0928d515d4f15369b.png)
大数据课程D7——hadoop的YARN
文章作者邮箱:yugongshiyesina.cn 地址:广东惠州 ▲ 本章节目的 ⚪ 了解YARN的概念和结构; ⚪ 掌握YARN的资源调度流程; ⚪ 了解Hadoop支持的资源调度器:FIFO、Capacity、Fair; ⚪ 掌握YA…...
![](https://img-blog.csdnimg.cn/img_convert/610ad85b14a4fa16869d24033846d52c.png)
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…...
![](https://www.ngui.cc/images/no-images.jpg)
【【51单片机DA转换模块】】
爆改直流电机,DA转换器 main.c #include <REGX52.H> #include "Delay.h" #include "Timer0.h"sbit DAP2^1;unsigned char Counter,Compare; //计数值和比较值,用于输出PWM unsigned char i;void main() {Timer0_Init();whil…...
![](https://www.ngui.cc/images/no-images.jpg)
[SQL挖掘机] - 字符串函数 - substring
介绍: substring函数是在mysql中用于提取字符串的一种函数。它接受一个字符串作为输入,并返回从该字符串中指定位置开始的一部分子串。substring函数可以用于获取字符串中的特定字符或子串,以便进行进一步的处理或分析。 用法: 下面是substring函数的…...
![](https://img-blog.csdnimg.cn/2effa5fa5d004d3caec79a7e53fc9888.png)
第一百一十六天学习记录:C++提高:STL-string(黑马教学视频)
string基本概念 string是C风格的字符串,而string本质上是一个类 string和char区别 1、char是一个指针 2、string是一个类,类内部封装了char*,管理这个字符串,是一个char型的容器。 特点: string类内部封装了很多成员方…...
![](https://img-blog.csdnimg.cn/img_convert/0b77814ae98ee133bc00e92663c40d6d.png)
Meta-Transformer 多模态学习的统一框架
Meta-Transformer是一个用于多模态学习的新框架,用来处理和关联来自多种模态的信息,如自然语言、图像、点云、音频、视频、时间序列和表格数据,虽然各种数据之间存在固有的差距,但是Meta-Transformer利用冻结编码器从共享标记空间…...
![](https://img-blog.csdnimg.cn/img_convert/fe2c2fe861c3d0f98b2e19bb29aa3411.jpeg)
tinkerCAD案例:24.Tinkercad 中的自定义字体
tinkerCAD案例: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…...
![](https://img-blog.csdnimg.cn/8bf9274ba5454be18e650aba4bd7ab37.png)
list与流迭代器stream_iterator
运行代码: //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…...
![](https://img-blog.csdnimg.cn/img_convert/f7481a43913772402fbb2c0e5de23aef.jpeg)
九耶:冯·诺伊曼体系
冯诺伊曼体系(Von Neumann architecture)是一种计算机体系结构,它由匈牙利数学家冯诺伊曼于1945年提出。冯诺伊曼体系是现代计算机体系结构的基础,几乎所有的通用计算机都采用了这种体系结构。 冯诺伊曼体系的核心思想是将计算机硬…...
探索UCI心脏病数据:利用R语言和h2o深度学习构建预测模型
一、引言 随着机器学习模型在实际应用中的广泛应用,人们对于模型的解释性和可理解性日益关注。可解释性机器学习是指能够清晰、透明地解释机器学习模型决策过程的一种方法和技术。在许多领域中,如医疗诊断、金融风险评估和自动驾驶等,解释模型…...
![](https://img-blog.csdnimg.cn/90bc8d473cca4c918d696e5d265c923c.png)
基于 moleculer 微服务架构的智能低代码PaaS 平台源码 可视化开发
低代码开发平台源码 低代码管理系统PaaS 平台 无需代码或通过少量代码就可以快速生成应用程序的开发平台。 本套低代码管理后台可以支持多种企业应用场景,包括但不限于CRM、ERP、OA、BI、IoT、大数据等。无论是传统企业还是新兴企业,都可以使用管理后台…...
![](https://img-blog.csdnimg.cn/38f31d1a06d2430280a8b6ffb48605a8.png)
xrdp登录显示白屏且红色叉
如上图所示,xrdp登录出现了红色叉加白屏,这是因为不正常关闭导致,解决方法其实挺简单的 #进入/usr/tmp cd /usr/tmp #删除对应用户的kdecache-** 文件(我这里使用的是kde桌面),例如删除ywj用户对应的文件 …...
![](https://img-blog.csdnimg.cn/ca82698fe0964ce7aac8e45ab8d93ba9.png)
Docker安装 Mysql 8.x 版本
文章目录 Docker安装 Mysql 8.0.22Mysql 创建账号并授权Mysql 数据迁移同版本数据迁移跨版本数据迁移 Mysql 5.x 版本与 Mysql 8.x版本是两个大版本,这里演示安装Mysql 8.x版本 Docker安装 Mysql 8.0.22 # 下载mysql $ docker pull mysql 默认安装最新…...
![](https://img-blog.csdnimg.cn/c613d9feaed74b1890fb1794bf2b1a39.gif#pic_center)
【数理知识】刚体 rigid body 及刚体的运动
文章目录 1 刚体2 刚体一般运动1 平移运动2 旋转运动 Ref 1 刚体 刚体是指在运动中和受力作用后,形状和大小不变,而且内部各点的相对位置不变的物体。绝对刚体实际上是不存在的,只是一种理想模型,因为任何物体在受力作用后&#…...
![](https://img-blog.csdnimg.cn/209627c9a2d345f79fda565706a005d0.png)
【UE5 多人联机教程】03-创建游戏
效果 步骤 打开“UMG_MainMenu”,增加创建房间按钮的点击事件 添加如下节点 其中,“FUNL Fast Create Widget”是插件自带的函数节点,内容如下: “创建会话”节点指游戏成功创建一个会话后,游戏的其他实例即可发现&am…...
![](https://www.ngui.cc/images/no-images.jpg)
【时间序列预测 】M4
【时间序列预测 】M4 论文题目:The M4 Competition: 100,000 time series and 61 forecasting methods 中文题目: 论文链接: 论文代码: 论文团队: 发表时间: DOI: 引用: 引用数: 摘要…...
![](https://www.ngui.cc/images/no-images.jpg)
SpringCloud微服务实战——搭建企业级开发框架(五十三):微信小程序授权登录增加多租户可配置界面
GitEgg框架集成weixin-java-miniapp工具包以实现微信小程序相关接口调用功能,weixin-java-miniapp底层支持多租户扩展。每个小程序都有唯一的appid,weixin-java-miniapp的多租户实现并不是以租户标识TenantId来区分的,而是在接口调用时&#…...
![](https://img-blog.csdnimg.cn/img_convert/07e5788e20a4e12a78357324d2f6504c.jpeg)
Stability AI推出Stable Diffusion XL 1.0,文本到图像模型
Stability AI宣布推出Stable Diffusion XL 1.0,这是一个文本到图像的模型,该公司将其描述为迄今为止“最先进的”版本。 Stability AI表示,SDXL 1.0能生成更加鲜明准确的色彩,在对比度、光线和阴影方面做了增强,可生成…...
![](https://img-blog.csdnimg.cn/9f726cf121d448568d50030f996d90bd.png#pic_center)
B076-项目实战--宠物上下架 展示 领养 收购订单
目录 上下架功能提供后台宠物列表实现 前台展示前台宠物列表和详情展示店铺展示 领养分析前台后端PetControllerPetServiceImpl 订单需求分析可能产生订单的模块订单模块额外功能 订单设计表设计流程设计 集成基础代码收购订单创建订单前端后端 上下架功能提供 后台宠物列表实…...
![](https://img-blog.csdnimg.cn/6f593ce025094df9ac055f304530930e.png)
【iOS】—— 持久化
文章目录 数据持久化的目的iOS中数据持久化方案数据持久化方式分类内存缓存磁盘缓存 沙盒机制获取应用程序的沙盒路径沙盒目录的获取方式 持久化数据存储方式XML属性列表Preferences偏好设置(UserDefaults)数据库存储什么是序列化和反序列化,…...
![](https://img-blog.csdnimg.cn/img_convert/8fde6ad07321c0078f5e29af71bcb73f.jpeg)
教程 - 在 Vue3+Ts 中引入 CesiumJS 的最佳实践@2023
1. 本篇适用范围与目的 1.1. 适用范围 严格使用 Vue3 TypeScript 的前端项目,包管理器默认使用 pnpm 构建工具使用 Vite4 使用原生 CesiumJS 依赖做应用开发 客户端渲染,因为我不太熟悉 Vue 的服务端渲染,有本篇的介绍后,熟悉…...
![](https://img-blog.csdnimg.cn/f4411797922f45d0a519ff2b48a5d6c7.png)
最优化方法
一. 图论 1.最小生成树 图的生成树是它的一颗含有其所有顶点的无环连通子图,一 幅加权图的最小生成树(MST)是它的一颗权值(树中的所有边的权值之和) 最小的生成树 • 适用场景:道路规划、通讯网络规划、管道铺设、电线布设等 题目数据 kruskal算法 稀疏图&#x…...
![](https://www.ngui.cc/images/no-images.jpg)
Mongodb 多文档聚合操作处理方法二(Map-reduce 函数)
聚合 聚合操作处理多个文档并返回计算结果。您可以使用聚合操作来: 将多个文档中的值分组在一起。 对分组数据执行操作以返回单个结果。 分析数据随时间的变化。 要执行聚合操作,您可以使用: 聚合管道 单一目的聚合方法 Map-reduce 函…...
![](https://www.ngui.cc/images/no-images.jpg)
ant design vue j-modal 修改高度
问题描述 今天在项目中遇到关于j-modal组件修改弹窗大小问题,我尝试使用直接使用:height"300",没用效果,弹窗大小依然和没改之前一样,后来找到了这种方式可以去修改j-modal弹窗大小,下面来看下代码实现&…...
![](https://www.ngui.cc/images/no-images.jpg)
spring学习笔记七
一、自动装配 1.1、BookDao接口和实现类 public interface BookDao {void save(); } public class BookDaoImpl implements BookDao {public void save(){System.out.println("book dao save......");} } 1.2、BookService接口和实现类 public interface BookSer…...
![](/images/no-images.jpg)
the7做的网站/人力资源培训
一、常用CDR快捷键【Ctrl】:【Ctrl】 【X】剪切【Ctrl】 【C】复制【Ctrl】 【V】粘贴【Ctrl】 【A】全选【Ctrl】 【S】保存【Ctrl】 【O】打开【Ctrl】 【N】新建【Ctrl】 【F4】关闭【Ctrl】 【Z】取消【Ctrl】 【Shift】 【Z】恢复【Ctrl]U】下划线【Ctrl]。】…...
![](/images/no-images.jpg)
做网站样式模板/网站权重如何查询
VB是一种由Basic语言进化的产物-----VisualBasic,其意思是可视化Basic。 VC是一种C语言进化出来的VisualC,意思是可视化C。...
![](http://img-03.proxy.5ce.com/view/image?&type=2&guid=d6b1bc3a-7e2f-eb11-8da9-e4434bdf6706&url=https://pic2.zhimg.com/v2-4e1735ddee905e3135f8f31b887ad3cd_b.jpg)
淄博网站制作哪家好/crm管理系统
Redis深度历险分为两个部分,单机Redis和分布式Redis。本文为分布式Redis深度历险系列的第一篇,主要内容为Redis的复制功能。Redis的复制功能的作用和大多数分布式存储系统一样,就是为了支持主从设计,主从设计的好处有以下几点&…...
![](http://blog.51cto.com/attachment/200909/200909181253243852578.jpg)
广州商务网站建设/最近最新新闻
这是我的祖国,我们的祖国,尽管现在还有许多不尽人意的地方,但我爱我的祖国,我们都爱我们的祖国!!我们的祖国一定会更加强大!!! 本文转自ycrsjxy51CTO博客,原文…...
![](/images/no-images.jpg)
做填写信息的超链接用什么网站/软文代写平台有哪些
简单介绍 Maven,在意第绪语中意为对知识的积累。Maven最初用来在Jakarta Turbine项目中简化该项目的构建过程。Jakarta Trubine项目有多个project。每一个project都有自己的多个Ant构建文件。每一个project的这些构建文件都仅仅有一小部分不同。而且全部的Jar文件被…...
![](/images/no-images.jpg)
vi设计的基本要素/培训机构seo
扩展ACL可以通过源地址、目标地址、协议、端口以及筛选动作等进行访问控制,相较于标准ACL,在网络层和传输层可以进行更全面的控制。对于其它访问控制,大家可以参考下面的语句的进行,也可添加几条语句(log/syn/log-input等)&#x…...