Windows 程序开机自启动速度优化,为什么腾讯会议自启动速度那么高?

news2024/11/24 6:39:57

目录

一、问题的说明和定义

二、问题的分析

1.问题初步分析

2.详细的分析:

2.1Windows常见的自启动方式

2.2Windows常见的自启动方式的细节分析

三、问题的解决方案

1、为什么腾讯会议Rooms那么快

2.我们是否可以跟腾讯会议一样快


一、问题的说明和定义

这两天有个优化项需要做个技术调研,就是我们项目的开机启动时间比较长,大概20多秒,但对比腾讯会议Rooms,这款软件的自启动就异常的快,用户登录几秒就可以看到程序界面。

拿到任务了,开干吧!!!!!!

二、问题的分析

1.问题初步分析

首先分析猜测慢的原因:

  • 从双击程序图标启动那一刻,到看到界面的过程比较耗时,这个做了验证,在4秒左右(这个可有具体负责的工程师单独优化),相比于二十多秒的启动时间,还是有很多其他因素需要优化的。
  • 操作系统的开机自启动有延迟,本文主要分析的是这种场景的耗时点,以及可能的解决方案。

2.详细的分析:

2.1Windows常见的自启动方式

知己知彼百战百胜,所以第一步,先学习了解Windows系统中自启动的方案都有哪些,常见的方式四个

  1. 指定文件夹中放入程序快捷方式,或者启动脚本bat文件。
  2. 注册表自启动方式
  3. 任务计划程序方式
  4. Windows任务自启动

针对上面四种的详细描述,网上一搜一大把,我这里就不在赘述了,但我想把我在分析过程中,针对这几种方案的差别,做个简单的描述

2.2Windows常见的自启动方式的细节分析


针对第一、二种,没啥好说的,个人感觉,私人电脑自己配置可以这么玩,简单粗暴,但针对一款应用软件的功能,这么做就不是很合适了,不推荐


第三种自启动方式,一共有五个注册表类型可以实现自启动,如下:

HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run

表示任何账户每一次登陆到Windows系统都会自动启动在这个项下面注册的程序

HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunOnce

表示任何账户下一次登陆到Windows系统会自动启动在这个项下面注册的程序,以后就不会自启了

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run

表示当前账户每一次登陆到Windows系统都会自动启动在这个项下面注册的程序

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunOnce

表示当前账户下一次登陆到Windows系统会自动启动在这个项下面注册的程序,以后就不会自启了

HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Run

表示程序需要已管理员权限启动

自启动耗时在哪里呢?

1、注册表自启动的程序是在用户登录后,是否可以提到用户登录前呢?这样就可以提升启动速度了。针对Win11来说,是可以的,win11有一个控制选项,设置里面,找账户

“使用我的登录信息在更新后自动完成设置” 该选项保持勾选,即可在用户登录前启动

win10应该没有该选项控制

2、windows操作系统有一个注册表的值StartupDelayInMS,可以控制在用户登录后,延迟若干秒后,在自启动应用程序,该机制主要是为了把计算机资源留给操作系统用,该字段默认是没有的,没有的情况下,操作系统的默认延时未10s,可以加上该字段,然后设置其为0,即可取消这10s的延时HKEY_USERS\S-1-5-21-1084762125-4268433165-138902895-1001\Software\Microsoft\Windows\CurrentVersion\Explorer\Serialize

Serialize项如果没有,需要添加一下,然后再该项下,新增一个DWORD的值StartupDelayInMS,设置16进制为0即可

3、如果自启动项比较多的时候,启动顺序是不可控的,这样肯定会有些排在后面,导致启动比较慢,该问题的解决方案会在后面说


第四种方式就是通过服务启动

网上制作服务的博客很多,不在赘述

windows 把exe 设置成服务运行_exe做成服务_小小的技术员的博客-CSDN博客

该方案的优点是,在计算机启动后,服务会优先启动,无论是否用户登录,缺点是从xp或者win7以后,不支持UI界面的服务,如果将UI程序制作为服务,那么UI部分也是不会启动的,原因可参看[笔记]Windows安全之《二》Session0隔离及相关启动技术_会话隔离_二进制怪兽的博客-CSDN博客

下面是启动指定app的服务程序,可参考

#include <iostream>
#include <Windows.h>
#include <process.h>
#include <processthreadsapi.h>
#include <tlhelp32.h>
#include <tchar.h>
#include <comutil.h>
#include <cwchar>
#include "ini.h"

#pragma warning(disable:4996)

HANDLE GetProcessHandle(int ID)
{
	return OpenProcess(PROCESS_ALL_ACCESS, FALSE, ID);
}

BOOL GetTokenByName(HANDLE& hToken, LPSTR lpName)
{
	if (!lpName)
	{
		return FALSE;
	}

	HANDLE	hProcessSnap = NULL;
	BOOL	bRet = FALSE;
	PROCESSENTRY32 pe32 = { 0 };
	HANDLE hProcess = NULL;

	hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (hProcessSnap == INVALID_HANDLE_VALUE)
	{
		return FALSE;
	}

	pe32.dwSize = sizeof(PROCESSENTRY32);

	if (Process32First(hProcessSnap, &pe32))
	{
		do
		{
			if (!strcmp(_strupr((char*)pe32.szExeFile), _strupr(lpName)))
			{
				hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pe32.th32ProcessID);
				bRet = OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, &hToken);
				CloseHandle(hProcess);
				CloseHandle(hProcessSnap);
				return (bRet);
			}
		} while (Process32Next(hProcessSnap, &pe32));
	}

	CloseHandle(hProcessSnap);
	return (bRet);
}

BOOL StartProcess(LPCSTR lpProcName)
{
	std::string CurSubProcessDir(lpProcName);

	CurSubProcessDir = CurSubProcessDir.substr(0, CurSubProcessDir.rfind('\\'));

	if (!lpProcName)
	{
		return FALSE;
	}

	HANDLE hToken = NULL;
	if (!GetTokenByName(hToken, (LPSTR)(LPCTSTR)("EXPLORER.EXE")))
	{
		return FALSE;
	}

	STARTUPINFO         stStartup;
	PROCESS_INFORMATION stProcInfo;

	ZeroMemory(&stStartup, sizeof(STARTUPINFO));
	stStartup.cb = sizeof(STARTUPINFO);
	stStartup.lpDesktop = (LPSTR)(LPCTSTR)("winsta0\\default");
	stStartup.dwFlags |= STARTF_USESHOWWINDOW;
	stStartup.wShowWindow = SW_SHOWNORMAL;

	BOOL bResult = CreateProcessAsUser(hToken, lpProcName, NULL, NULL, NULL,
		FALSE, NORMAL_PRIORITY_CLASS, NULL, CurSubProcessDir.c_str(), &stStartup, &stProcInfo);

	CloseHandle(hToken);

	if (!bResult)
	{
		char info[80];
		sprintf(info, "CreateProcess failed. error code = %d", GetLastError());
	}

	return bResult;
}

int KillProgram(LPCSTR lpProcName)
{
	const char* strFile = NULL;
	HANDLE         hProcessSnap = NULL;
	PROCESSENTRY32 stProcEntry32 = { 0 };

	if (!lpProcName || !lpProcName[0])
	{
		return 1; // 参数错误
	}

	strFile = strrchr((const char*)lpProcName, '\\');
	if (0 != strFile)
	{
		lpProcName = strFile + 1;
	}
	hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (hProcessSnap == (HANDLE)-1)
	{
		return 3;  // 调用失败
	}

	//用来给stProcEntry32空间赋值0
	memset(&stProcEntry32, 0, sizeof(stProcEntry32));
	stProcEntry32.dwSize = sizeof(PROCESSENTRY32);

	if (Process32First(hProcessSnap, &stProcEntry32))
	{
		do
		{
			if (_strcmpi(stProcEntry32.szExeFile, lpProcName) == 0 ||
				_strcmpi(stProcEntry32.szExeFile, "WerFault.exe") == 0)  //Windows 操作系统将错误处理从崩溃进程的上下文移至新服务,即 Windows 错误报告(WER)
			{
				HANDLE hTerminate = GetProcessHandle(stProcEntry32.th32ProcessID);

				TerminateProcess(hTerminate, 999);
				CloseHandle(hTerminate);
				Sleep(10);
			}
		} while (Process32Next(hProcessSnap, &stProcEntry32));
	}
	CloseHandle(hProcessSnap);

	return 0;
}

DWORD FindProcessId(char* processName)
{
	// strip path

	char* p = strrchr(processName, '\\');
	if (p)
		processName = p + 1;

	PROCESSENTRY32 processInfo;
	processInfo.dwSize = sizeof(processInfo);

	HANDLE processesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
	if (processesSnapshot == INVALID_HANDLE_VALUE)
		return 0;

	Process32First(processesSnapshot, &processInfo);
	if (!strcmp(processName, processInfo.szExeFile))
	{
		CloseHandle(processesSnapshot);
		return processInfo.th32ProcessID;
	}

	while (Process32Next(processesSnapshot, &processInfo))
	{
		if (!strcmp(processName, processInfo.szExeFile))
		{
			CloseHandle(processesSnapshot);
			return processInfo.th32ProcessID;
		}
	}

	CloseHandle(processesSnapshot);
	return 0;
}

int main()
{
	ini::iniReader config;
	std::string tmp = _pgmptr;
	tmp = tmp.erase(tmp.find_last_of("\\"));
	bool ret = config.ReadConfig(tmp + "\\config.ini");
	if (ret == false)
	{
		printf("ReadConfig is Error,Cfg=%s", "config.ini");
		return 1;
	}
	std::string apppath = config.ReadString("group1", "path", "");
	std::string appname = config.ReadString("group1", "application", "");

	while (!FindProcessId((char *)appname.c_str())) {
		Sleep(200);
		StartProcess((apppath + appname).c_str());
		int a = 1;
	}
	
	return 1;
}

#ifndef INI_H_
#define INI_H_
#include <string>
#include <map>
#include<fstream>

namespace ini
{
	class iniReader
	{
	public:
		iniReader()
		{
		}
		~iniReader()
		{
		}
		bool ReadConfig(const std::string& filename)
		{
			settings_.clear();
			std::ifstream infile(filename.c_str());//构造默认调用open,所以可以不调用open
			//std::ifstream infile;
			//infile.open(filename.c_str());
			//bool ret = infile.is_open()
			if (!infile) {
				return false;
			}
			std::string line, key, value, section;
			std::map<std::string, std::string> k_v;
			std::map<std::string, std::map<std::string, std::string> >::iterator it;
			while (getline(infile, line))
			{
				if (AnalyseLine(line, section, key, value))
				{
					it = settings_.find(section);
					if (it != settings_.end())
					{
						k_v[key] = value;
						it->second = k_v;
					}
					else
					{
						k_v.clear();
						settings_.insert(std::make_pair(section, k_v));
					}
				}
				key.clear();
				value.clear();
			}
			infile.close();
			return true;
		}

		std::string ReadString(const char* section, const char* item, const char* default_value)
		{
			std::string tmp_s(section);
			std::string tmp_i(item);
			std::string def(default_value);
			std::map<std::string, std::string> k_v;
			std::map<std::string, std::string>::iterator it_item;
			std::map<std::string, std::map<std::string, std::string> >::iterator it;
			it = settings_.find(tmp_s);
			if (it == settings_.end())
			{
				//printf("111");
				return def;
			}
			k_v = it->second;
			it_item = k_v.find(tmp_i);
			if (it_item == k_v.end())
			{
				//printf("222");
				return def;
			}
			return it_item->second;
		}

		int ReadInt(const char* section, const char* item, const int& default_value)
		{
			std::string tmp_s(section);
			std::string tmp_i(item);
			std::map<std::string, std::string> k_v;
			std::map<std::string, std::string>::iterator it_item;
			std::map<std::string, std::map<std::string, std::string> >::iterator it;
			it = settings_.find(tmp_s);
			if (it == settings_.end())
			{
				return default_value;
			}
			k_v = it->second;
			it_item = k_v.find(tmp_i);
			if (it_item == k_v.end())
			{
				return default_value;
			}
			return atoi(it_item->second.c_str());
		}

		float ReadFloat(const char* section, const char* item, const float& default_value)
		{
			std::string tmp_s(section);
			std::string tmp_i(item);
			std::map<std::string, std::string> k_v;
			std::map<std::string, std::string>::iterator it_item;
			std::map<std::string, std::map<std::string, std::string> >::iterator it;
			it = settings_.find(tmp_s);
			if (it == settings_.end())
			{
				return default_value;
			}
			k_v = it->second;
			it_item = k_v.find(tmp_i);
			if (it_item == k_v.end())
			{
				return default_value;
			}
			return atof(it_item->second.c_str());
		}

	private:
		bool IsSpace(char c)
		{
			if (' ' == c || '\t' == c)
				return true;
			return false;
		}

		bool IsCommentChar(char c)
		{
			switch (c)
			{
			case '#':
				return true;
			default:
				return false;
			}
		}

		void Trim(std::string& str)
		{
			if (str.empty())
			{
				return;
			}
			int i, start_pos, end_pos;
			for (i = 0; i < str.size(); ++i) {
				if (!IsSpace(str[i])) {
					break;
				}
			}
			if (i == str.size())
			{
				str = "";
				return;
			}
			start_pos = i;
			for (i = str.size() - 1; i >= 0; --i) {
				if (!IsSpace(str[i])) {
					break;
				}
			}
			end_pos = i;
			str = str.substr(start_pos, end_pos - start_pos + 1);
		}

		bool AnalyseLine(const std::string& line, std::string& section, std::string& key, std::string& value)
		{
			if (line.empty())
				return false;
			int start_pos = 0, end_pos = line.size() - 1, pos, s_startpos, s_endpos;
			if ((pos = line.find("#")) != -1)
			{
				if (0 == pos)
				{
					return false;
				}
				end_pos = pos - 1;
			}
			if (((s_startpos = line.find("[")) != -1) && ((s_endpos = line.find("]"))) != -1)
			{
				section = line.substr(s_startpos + 1, s_endpos - 1);
				return true;
			}
			std::string new_line = line.substr(start_pos, start_pos + 1 - end_pos);
			if ((pos = new_line.find('=')) == -1)
				return false;
			key = new_line.substr(0, pos);
			value = new_line.substr(pos + 1, end_pos + 1 - (pos + 1));
			Trim(key);
			if (key.empty()) {
				return false;
			}
			Trim(value);
			if ((pos = value.find("\r")) > 0)
			{
				value.replace(pos, 1, "");
			}
			if ((pos = value.find("\n")) > 0)
			{
				value.replace(pos, 1, "");
			}
			return true;
		}

	private:
		//std::map<std::string, std::string> settings_;
		std::map<std::string, std::map<std::string, std::string> >settings_;
	};
}
#endif
[group1]
path = C:\\Users\\EEO\\source\\repos\\Project2\\Debug\\
application = Project2.exe

三、问题的解决方案

下面就如何优化启动时间,做一个详细的说明

1、为什么腾讯会议Rooms那么快

在观察了腾讯会议的开机自启动现象后,发现他的启动非常稳定,都是在开机登录后,五到十秒间完成自启动,在此同时,其他软件都比较慢,如有道云笔记、企业微信等等,那为什么会有如此现象呢?

为了探究该问题的原因,初步分析了腾讯会议是如何实现自启动的,发现他也是同过注册表实现的,只不过他是在WOW6432Node下面,然后就怀疑莫非在这个节点下比较快

 为了验证猜想,制作了一个demo程序,没有任何复杂逻辑,启动后单纯的显示一个窗口,可排除软件自身效率引入的问题干扰,但通过实验发现,启动时间和其他比较慢的程序一样。进一步分析,为了排除那些未知因素,将demo的启动放入腾讯会议的注册表中

结果依然比较慢,那么到底是为什么腾讯会议就启动的比别的软件快呢?还做了其他无用的验证,比如将腾讯会议的启动项和demo调换等等,不在赘述,这些最终都没定位到真实原因。

偶然间发现,在自启动后,将腾讯会议最小化,过个十几二十秒后,腾讯会议会自动再次弹出来,而且这个时间点和其他自启动比较慢的软件的时间点是一致的,所以怀疑其在注册表启动前,通过某种手段已经启动了一次程序,那么是那种手段呢?自启动常见的一共也就上面四种,排查了本地电脑方式一和方式二中没有找到腾讯会议相关的设置,那么剩下方式四了,翻看腾讯会议安装包中的都有哪些exe文件,结合资源管理器中腾讯会议相关的进程,然后再windows服务列表中查找 

 

最终定位到可疑服务 

为了验证猜想,将该服务设置为手动启动,然后发现,腾讯会议的启动和其他程序没有什么差别,都很慢,至此,腾讯会议为什么比较快差不多有结论了。

猜测腾讯会议快速自启动的实现方案:通过开机自启的服务,拉起腾讯会议的应用程序,怎么实现的,可参考前文。(至于腾讯会议是否是这么玩的,这个就不清楚了,但能确定的是,肯定和服务有关)

2.我们是否可以跟腾讯会议一样快

可以的,虽然直接将UI程序做成服务,在启动后循环检测目标进程是否启动,没有了尝试启动,已启动就可以退出服务了。为什么要这么做呢,是因为在实际的验证过程中,发现,在登录前试图拉起目标进程总是失败。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue之插件的定义和使用

概述 学习本文之前&#xff0c;我们需要弄清楚何为插件&#xff1f;插件其实就是一段扩展程序&#xff0c;主要目的是用于扩展功能。就比如Idea家族和VSCode家族的插件&#xff0c;它们也是一段扩展程序&#xff0c;将其安装到IDE中就可以使用插件里面实现的功能了&#xff0c…

SCADA平台的HMI功能

01 前言 虹科Panorama SCADA平台支持桌面HMI、Web HMI和移动HMI的功能。桌面HMI主要是在桌面工作站实现数据可视化&#xff0c;能够获取到最全面的数据信息以及实现功能&#xff1b;Web HMI可以通过在软件中添加Web HMI服务器&#xff0c;运行程序后&#xff0c;可以在Web 客户…

HTML5基础知识总结总结(详细,附带源代码)

HTML5基础知识 一&#xff1a;前言二&#xff1a; HTML基本结构。三&#xff1a;基本标签3.1 h标签3.2 p标签3.3 hr标签3.4 br标签3.5 strong标签与em标签3.6 特殊符号3.7 运行效果 三&#xff1a;图像标签四&#xff1a;链接标签五&#xff1a;列表六&#xff1a;表格七&#…

Allegro PCB后处理和生产文件导出

Allegro PCB后处理&#xff0c;主要是完成线路设计以后&#xff0c;输出生产文件之前的处理。部分是看教程做的记录&#xff0c;方便以后自己参考。 教程&#xff1a; [小哥Cadence Allegro 132讲字幕版PCB视频教程]_哔哩哔哩_bilibili 感觉关键是多看右边Options菜单&#xf…

【15】SCI易中期刊推荐——电子电气 | 仪器仪表(中科院4区)

💖💖>>>加勒比海带,QQ2479200884<<<💖💖 🍀🍀>>>【YOLO魔法搭配&论文投稿咨询】<<<🍀🍀 ✨✨>>>学习交流 | 温澜潮生 | 合作共赢 | 共同进步<<<✨✨ 📚📚>>>人工智能 | 计算机视觉…

[NISACTF 2022]level-up

[NISACTF 2022]level-up f12 发现提示disallow 也就是不允许的想到robots.txt LEVEL_2 <?php //here is level 2 error_reporting(0); #屏蔽报错信息 include "str.php"; #包含str.php这个页面 if (isset($_POST[array1]) && isset($_POST[arr…

巧用千寻位置GNSS软件|如何快速完成道路桥涵放样

道路桥涵放样主要解决道路施工中正交、斜交涵洞的测量&#xff0c;正交涵洞放样是中心线的定线放样&#xff1b;斜交涵洞放养是涵洞中心线与线路成一固定夹角的情况下的放样。 那么如何运用千寻位置GNSS软件实现道路桥涵放样呢&#xff1f;下面为各位一一介绍。 点击【测量】-&…

【移动端网页布局】移动端网页布局基础概念 ⑨ ( webkit 内核 | 移动端网页 CSS 初始化 - normalize.css )

文章目录 一、webkit 内核二、移动端网页 CSS 初始化 - normalize.css 一、webkit 内核 移动端浏览器 都是 基于 webkit 内核的 , QQ 浏览器 / 百度 / Safari / UC 都是基于 webkit 内核的 ; 移动端网页布局需要 兼容 普通浏览器 与 webkit 浏览器 ; webkit 内核浏览器 对 HT…

Ubuntu18 更换 apt 源为阿里云

Step1. 备份 list 文件 进入对应文件夹&#xff0c;用管理员权限&#xff0c;执行复制操作&#xff1a; cd /etc/apt/ sudo cp sources.list sources.list.bak Step2. 修改 list 文件 通过管理员权限&#xff0c;使用 vim 进行修改&#xff1a; sudo vim sources.list 将…

【Pytorch】六行代码实现:特征图提取与特征图可视化

前言 之前记录过特征图的可视化&#xff1a;Pytorch实现特征图可视化&#xff0c;当时是利用IntermediateLayerGetter 实现的&#xff0c;但是有很大缺陷&#xff0c;只能获取到一级的子模块的特征图输出&#xff0c;无法获取内部二级子模块的输出。今天补充另一种Pytorch官方…

ChatGPT如何写作-ChatGPT写作程序

ChatGPT如何写作 ChatGPT是一款自然语言处理模型&#xff0c;它无法像人类一样进行“写作”。但是&#xff0c;您可以利用ChatGPT的生成文本功能来帮助您生成文字。以下是一些使用ChatGPT写作的建议&#xff1a; 确定主题和目标受众。在开始写作之前&#xff0c;请确保您清楚知…

凝心聚力,携“源”出海:开源社顾问委员会2023年第一季度会议圆满举办!

2023 年 3 月 25 日&#xff0c;开源社顾问委员会&#xff08;以下简称"顾问委员会"&#xff09;第一季度会议在北京圆满召开。这是顾问委员会自 2018 年成立以来的第 17 次全体委员会议。 为增进顾问委员会成员交流&#xff0c;加强开源社社区建设&#xff0c;实现开…

第五章 资源包使用

游戏开发中会大量使用模型文件&#xff0c;图片文件&#xff0c;这些资源都需要事先导入到项目中去。导入的方式非常简单&#xff0c;将这些文件直接复制到项目中的Assets目录下即可。Unity 会在文件添加到 Assets 文件夹时自动检测到这些文件并同步显示在Project视图中。 Uni…

内分泌失调对身体有什么影响?

体内各种荷尔蒙的平衡&#xff0c;可以维持内分泌的稳定&#xff0c;当生活节奏被打乱&#xff0c;就会导致熬夜、入睡困难、压力过大、不按时就餐、久坐、情绪不稳定等。 对此&#xff0c;内分泌失调都是不小的问题&#xff0c;都是会影响身体的各个部位的。 内分泌对身体有什…

【U8+】用友U8+产品-操作系统、数据库、浏览器推荐支持一览表

【业务场景】 大家平时在服务、实施过程中&#xff0c; 经常被问到各个版本的产品支持什么版本操作系统、数据库、浏览器&#xff1f; 根据各个版本发版说明&#xff0c; 总结了操作系统、数据库、浏览器推荐使用一览表。 软件版本与电脑操作系统版本相辅相承&#xff0c; 一方…

Redis 缓存穿透、缓存雪崩、缓存击穿

缓存穿透 缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在&#xff0c;这样缓存永远不会生效&#xff0c;这些请求都会打到数据库。 常见的解决方案有两种&#xff1a; 缓存空对象 优点&#xff1a;实现简单&#xff0c;维护方便 缺点&#xff1a; 额外的内存消耗 可…

chatGPT写文章一半不写了-如何让chatGPT写完整文章

chatGPT不生成内容的原因有哪些 当ChatGPT不生成内容时&#xff0c;可能有如下原因&#xff1a; 数据限制&#xff1a;ChatGPT的生成能力是建立在其训练数据的基础上的。如果输入的内容领域、主题和题材不在其数据范围内&#xff0c;ChatGPT将无法生成非常有意义和具体的内容。…

图像修补论文阅读:MAT算法笔记

标题&#xff1a;MAT: Mask-Aware Transformer for Large Hole Image Inpainting 会议&#xff1a;CVPR2022 论文地址&#xff1a;https://ieeexplore.ieee.org/document/9879508/ 官方代码&#xff1a;https://github.com/fenglinglwb/MAT 作者单位&#xff1a;香港中文大学、…

第十一讲 常用数据结构之字符串

第二次世界大战促使了现代电子计算机的诞生&#xff0c;世界上的第一台通用电子计算机名叫 ENIAC&#xff08;电子数值积分计算机&#xff09;&#xff0c;诞生于美国的宾夕法尼亚大学&#xff0c;占地167平米&#xff0c;重量约27吨&#xff0c;每秒钟大约能够完成约5000次浮点…

Ansible基础和命令行模块操作

目录 1.Ansible介绍 1.Ansible能做什么&#xff1f; 2.Ansible的特性和原理 2.Ansible部署 3.Ansible命令模块 1.command模块 2.shell模块 3.cron模块 4.user模块 5.group模块 7.file模块 8.hostname 模块 9.ping 模块 10. yum 模块 11.service/systemd 模块 1…