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

线程和进程的区别及基础线程创建

1 线程和进程的区别

  1. 资源分配和调度:

    • 进程(火车)是操作系统进行资源分配和调度的最小单位。它有自己的独立资源空间,包括内存、文件句柄等。
    • 线程(车厢)是CPU调度的最小单位。一个进程可以包含多个线程,它们共享相同的资源空间,如内存,但有各自的执行路径。
  2. 数据共享:

    • 进程间的数据共享相对困难,需要使用进程间通信(IPC)的机制。
    • 同一进程内的不同线程可以直接共享数据,因为它们共享相同的内存空间。
  3. 稳定性和影响范围:

    • 进程间是相互独立的,一个进程的崩溃通常不会影响其他进程。
    • 同一进程下的线程是共享相同的进程资源,一个线程的崩溃可能导致整个进程崩溃。
  4. 资源消耗和扩展性:

    • 进程比线程消耗更多的计算机资源,因为它们有独立的资源空间。
    • 进程可以在多台机器上运行,而线程适合在多核处理器上运行。
  5. 内存地址管理:

    • 进程的内存地址可以上锁,实现互斥访问,确保线程安全。
    • 进程的内存地址可以限定使用量,通过信号量等机制控制资源的访问。

这个比喻涵盖了进程和线程的许多关键概念,希望能够更好地帮助理解它们之间的区别和关系。

2 基础线程创建

2.1 代码示例

以下最简单的事例来理解线程:

//test.cpp
#include <iostream>
#include <pthread.h>  //必须的头文件using namespace std;#define NUM_THREADS 5// 线程的运行函数
void* say_hello(void* args)
{cout << "Hello Runoob!" << endl;return 0;
}int main()
{// 定义线程的 id 变量,多个变量使用数组pthread_t tids[NUM_THREADS];for(int i = 0; i < NUM_THREADS; ++i){//参数依次是:创建的线程id,线程参数,调用的函数,传入的函数参数int ret = pthread_create(&tids[i], NULL, say_hello, NULL);if (ret != 0){cout << "pthread_create error: error_code=" << ret << endl;}}//等各个线程退出后,进程才结束,否则进程强制结束了,线程可能还没反应过来;//**当linux中,主线程以pthread_exit(NULL)作为返回值,则主线程会等待子线程。**pthread_exit(NULL);}

2.2 线程代码编译

2.2.1 命令行编译

使用 -lpthread 库编译下面的程序:

$ g++ test.cpp -lpthread -o test.o

2.2.2 Makefile编译

但是如果用Makefile一堆文件编译的情况下如何设置

# Makefile中变量定义如:标签=内容,后面的变量引用需要$(标签)
GPU=0
CUDNN=0
OPENCV=0
OPENMP=0 # OpenMP是一套支持跨平台共享内存方式的多线程并发的编程API
DEBUG=0ARCH= -gencode arch=compute_30,code=sm_30 \-gencode arch=compute_35,code=sm_35 \-gencode arch=compute_50,code=[sm_50,compute_50] \-gencode arch=compute_52,code=[sm_52,compute_52]
#      -gencode arch=compute_20,code=[sm_20,sm_21] \ This one is deprecated?# This is what I use, uncomment if you know your arch and want to specify
# ARCH= -gencode arch=compute_52,code=compute_52VPATH=./src/:./examples
SLIB=libdarknet.so #待生成的动态链接库
ALIB=libdarknet.a  #待生成的静态链接库
EXEC=darknet #待生成的执行文件名称
OBJDIR=./obj/ #生成目标文件的目录,即当期目录下obj子目录CC=gcc
CPP=g++
NVCC=nvcc #nvidia编译器
AR=ar 	#ar,Linux系统的一个备份压缩命令,用于创建、修改备存文件(archive)。这里的ar命令是将目标文件打包为静态链接库。
ARFLAGS=rcs #ar命令的参数 -rcs。-r:将文件插入备存文件中 c:建立备存文件 s:若备存文件中包含了对象模式,可利用此参数建立备存文件的符号表
OPTS=-Ofast
#编译选项中指定 -pthread 会附加一个宏定义 -D_REENTRANT,该宏会导致 libc 头文件选择那些thread-safe的实现;
#链接选项中指定 -pthread 则同 -lpthread 一样,只表示链接 POSIX thread 库。由于 libc 用于适应 thread-safe 的宏定义可能变化,
#因此在编译和链接时都使用 -pthread 选项而不是传统的 -lpthread 能够保持向后兼容,并提高命令行的一致性。
LDFLAGS= -lm -pthread 
COMMON= -Iinclude/ -Isrc/
#-fPIC:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。
#unknow-pragmas:使用未知的#pragma指令
CFLAGS=-Wall -Wno-unused-result -Wno-unknown-pragmas -Wfatal-errors -fPIC ifeq ($(OPENMP), 1) #条件判断
CFLAGS+= -fopenmp
endififeq ($(DEBUG), 1) 
OPTS=-O0 -g #-g选项可以产生带有调试信息的目标代码
endifCFLAGS+=$(OPTS)ifeq ($(OPENCV), 1) 
COMMON+= -DOPENCV #相当于C语言中的#define OPENCV
CFLAGS+= -DOPENCV
LDFLAGS+= `pkg-config --libs opencv` -lstdc++  #pkg-config能够把头文件和库文件的位置指出来,给编译器使用
COMMON+= `pkg-config --cflags opencv` 
endififeq ($(GPU), 1) 
COMMON+= -DGPU -I/usr/local/cuda/include/
CFLAGS+= -DGPU
LDFLAGS+= -L/usr/local/cuda/lib64 -lcuda -lcudart -lcublas -lcurand
endififeq ($(CUDNN), 1) 
COMMON+= -DCUDNN 
CFLAGS+= -DCUDNN
LDFLAGS+= -lcudnn
endif#OBJ表示需要生成的目标文件
OBJ=gemm.o utils.o cuda.o deconvolutional_layer.o convolutional_layer.o list.o image.o activations.o im2col.o col2im.o blas.o crop_layer.o dropout_layer.o maxpool_layer.o softmax_layer.o data.o matrix.o network.o connected_layer.o cost_layer.o parser.o option_list.o detection_layer.o route_layer.o upsample_layer.o box.o normalization_layer.o avgpool_layer.o layer.o local_layer.o shortcut_layer.o logistic_layer.o activation_layer.o rnn_layer.o gru_layer.o crnn_layer.o demo.o batchnorm_layer.o region_layer.o reorg_layer.o tree.o  lstm_layer.o l2norm_layer.o yolo_layer.o iseg_layer.o image_opencv.o
EXECOBJA=captcha.o lsd.o super.o art.o tag.o cifar.o go.o rnn.o segmenter.o regressor.o classifier.o coco.o yolo.o detector.o nightmare.o instance-segmenter.o darknet.o
ifeq ($(GPU), 1) 
LDFLAGS+= -lstdc++ 
OBJ+=convolutional_kernels.o deconvolutional_kernels.o activation_kernels.o im2col_kernels.o col2im_kernels.o blas_kernels.o crop_layer_kernels.o dropout_layer_kernels.o maxpool_layer_kernels.o avgpool_layer_kernels.o
endifEXECOBJ = $(addprefix $(OBJDIR), $(EXECOBJA)) #addprefix统一添加前缀
OBJS = $(addprefix $(OBJDIR), $(OBJ))
DEPS = $(wildcard src/*.h) Makefile include/darknet.h #wildcard展开src文件下所有后缀为.h的头文件;all: obj backup results $(SLIB) $(ALIB) $(EXEC) #主要任务all和其所有依赖
#all: obj  results $(SLIB) $(ALIB) $(EXEC)$(EXEC): $(EXECOBJ) $(ALIB)$(CC) $(COMMON) $(CFLAGS) $^ -o $@ $(LDFLAGS) $(ALIB) #$@自动变量,表示目标名称,这里指$(EXEC),实际为darknet;$^指代所有前置条件,之间以空格分隔$(ALIB): $(OBJS)$(AR) $(ARFLAGS) $@ $^ $(SLIB): $(OBJS)$(CC) $(CFLAGS) -shared $^ -o $@ $(LDFLAGS) #-shared 该选项指定生成动态连接库(让连接器生成T类型的导出符号表,有时候也生成弱连接W类型的导出符号),不用该标志外部程序无法连接。相当于一个可执行文件$(OBJDIR)%.o: %.cpp $(DEPS)$(CPP) $(COMMON) $(CFLAGS) -c $< -o $@ #$<指代第一个前置条件,这里指%.cpp; -c编译、汇编到目标代码,不进行链接$(OBJDIR)%.o: %.c $(DEPS)$(CC) $(COMMON) $(CFLAGS) -c $< -o $@$(OBJDIR)%.o: %.cu $(DEPS)$(NVCC) $(ARCH) $(COMMON) --compiler-options "$(CFLAGS)" -c $< -o $@obj:mkdir -p obj #-p 确保目录名称存在,不存在的就建一个
backup:mkdir -p backup
results:mkdir -p results.PHONY: cleanclean:rm -rf $(OBJS) $(SLIB) $(ALIB) $(EXEC) $(EXECOBJ) $(OBJDIR)/*

3 线程属性

1、pthread_attr_init
功能: 对线程属性变量的初始化。2、pthread_attr_setscope
功能: 设置线程 __scope 属性。scope属性表示线程间竞争CPU的范围,也就是说线程优先级的有效范围。POSIX的标准中定义了两个值:PTHREAD_SCOPE_SYSTEM和PTHREAD_SCOPE_PROCESS,前者表示与系统中所有线程一起竞争CPU时间,后者表示仅与同进程中的线程竞争CPU。默认为PTHREAD_SCOPE_PROCESS。目前LinuxThreads仅实现了PTHREAD_SCOPE_SYSTEM一值。3、pthread_attr_setdetachstate
功能: 设置线程detachstate属性。该表示新线程是否与进程中其他线程脱离同步,如果设置为PTHREAD_CREATE_DETACHED则新线程不能用pthread_join()来同步,且在退出时自行释放所占用的资源。缺省为PTHREAD_CREATE_JOINABLE状态。这个属性也可以在线程创建并运行以后用pthread_detach()来设置,而一旦设置为PTHREAD_CREATE_DETACH状态(不论是创建时设置还是运行时设置)则不能再恢复到PTHREAD_CREATE_JOINABLE状态。4、pthread_attr_setschedparam
功能: 设置线程schedparam属性,即调用的优先级。5、pthread_attr_getschedparam
功能: 得到线程优先级。
#include <stdlib.h>   
#include <stdio.h>   
#include <errno.h>   
#include <pthread.h>   
static void pthread_func_1 (void);   
static void pthread_func_2 (void);   int main (int argc, char** argv)   
{   pthread_t pt_1 = 0;   pthread_t pt_2 = 0;   pthread_attr_t atrr = {0};   int ret = 0;   //初始化属性线程属性pthread_attr_init (&attr);   pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM);   pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);   ret = pthread_create (&pt_1, &attr, pthread_func_1, NULL);   if (ret != 0)   {   perror ("pthread_1_create");   }  
**//线程一的线程函数一结束就自动释放资源(因为设置了分离属性)**ret = pthread_create (&pt_2, NULL, pthread_func_2, NULL);   if (ret != 0)   {   perror ("pthread_2_create");   }   **//线程二就得等到pthread_join来释放系统资源(因为没有设置分离属性,需要依赖主线程释放资源)**pthread_join (pt_2, NULL);   return 0;   
}   static void pthread_func_1 (void)   
{   int i = 0;   for (; i < 6; i++)   {    printf ("This is pthread_1.\n");   if (i == 2)   {   pthread_exit (0);   }   }   return;   
}   static void pthread_func_2 (void)   
{   int i = 0;   for (; i < 3; i ++)   {   printf ("This is pthread_2.\n");   }   return;   
}

4 注意内存泄漏问题

在使用pthread_create函数创建线程时,默认情况下线程是非分离属性的,其分离属性由pthread_create函数的第二个参数决定。在非分离的情况下,当一个线程结束时,它所占用的系统资源并没有完全释放,也没有真正终止。

  • 只有在调用pthread_join函数并在其返回时,该线程才会释放自己的资源。
  • 或者在设置为分离属性的情况下,一个线程结束会立即释放它所占用的资源。

为了确保在创建线程后避免内存泄漏,必须规范地使用pthread_create,具体做法是设置线程为分离属性。以下是一个示例代码:

void run() {return;
}int main(){pthread_t thread;           // 创建线程idpthread_attr_t attr;        // 设置线程属性pthread_attr_init(&attr);    // 初始化线程属性pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); // 设置分离属性// 也可以使用 pthread_attr_setdetachstate(&attr, 1); 来设置分离属性pthread_create(&thread, &attr, run, 0); // 第二个参数决定了分离属性// ......return 0;
}

通过设置线程为分离属性,可以确保在线程结束时即刻释放其占用的资源,有效地防止内存泄漏问题。

5 总结

本文深入探讨了线程和进程的概念,以及它们之间的关键区别。通过一个简单而生动的比喻,将进程比喻为火车,线程比喻为火车的车厢,为读者提供了直观的理解。总体而言,深入理解线程和进程的概念,并掌握基础线程创建和属性设置,对于并发编程和多线程应用的开发至关重要。通过本文的阐述,读者将更好地理解这些概念,并能够在实际应用中更有效地利用线程和进程的优势。

参考

  • 线程属性pthread_attr_t简介_猴子居士的博客-CSDN博客_pthread_attr_t详解

  • pthread详解_networkhunter的博客-CSDN博客

  • [C++]多线程: 教你写第一个线程_cn_wk的专栏-CSDN博客_c++怎么写多线程

  • C++ 多线程 | 菜鸟教程 (runoob.com)

  • 编译条件“-lpthread”应该加在makefile的哪里阿?-CSDN社区

  • Darknet yolov3 Makefile文件解析_To Be Continue-CSDN博客

  • 多线程之pthread_create()函数_wushuomin的博客-CSDN博客_pthread_create

相关文章:

线程和进程的区别及基础线程创建

1 线程和进程的区别 资源分配和调度&#xff1a; 进程&#xff08;火车&#xff09;是操作系统进行资源分配和调度的最小单位。它有自己的独立资源空间&#xff0c;包括内存、文件句柄等。线程&#xff08;车厢&#xff09;是CPU调度的最小单位。一个进程可以包含多个线程&…...

如何使用postman进行接口调试

使用Postman进行接口调试 有些时候我们写代码的时候&#xff0c;会发现接口有报错&#xff0c;提示参数错误&#xff0c;我们为了更好的排查错误原因&#xff0c;可以在Postman上进行接口调试。将url&#xff0c;请求方式&#xff0c;参数&#xff0c;cookie都填写到Postman中…...

Leetcode 198 打家劫舍

题意理解&#xff1a; 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的房屋在同一晚上被小偷闯入&#xff0c;系统会自动报警。 给定一个代…...

相机图像质量研究(9)常见问题总结:光学结构对成像的影响--工厂镜头组装

系列文章目录 相机图像质量研究(1)Camera成像流程介绍 相机图像质量研究(2)ISP专用平台调优介绍 相机图像质量研究(3)图像质量测试介绍 相机图像质量研究(4)常见问题总结&#xff1a;光学结构对成像的影响--焦距 相机图像质量研究(5)常见问题总结&#xff1a;光学结构对成…...

Linux内核与驱动面试经典“小”问题集锦(5)

接前一篇文章&#xff1a;Linux内核与驱动面试经典“小”问题集锦&#xff08;4&#xff09; 问题6 问&#xff1a;mutex_lock和mutex_lock_interruptible的区别是什么&#xff1f; 备注&#xff1a;此问题也是笔者近期参加蔚来面试时遇到的一个问题。 答&#xff1a; 尽管…...

基于51 单片机的交通灯系统 源码+仿真+ppt

主要内容&#xff1a; 1&#xff09;南北方向的绿灯、东西方向的红灯同时亮40秒。 2&#xff09;南北方向的绿灯灭、黄灯亮5秒&#xff0c;同时东西方向的红灯继续亮。 3&#xff09;南北方向的黄灯灭、左转绿灯亮&#xff0c;持续20秒&#xff0c;同时东西方向的红灯继续…...

【蓝桥杯冲冲冲】[NOIP2017 提高组] 宝藏

蓝桥杯备赛 | 洛谷做题打卡day29 文章目录 蓝桥杯备赛 | 洛谷做题打卡day29[NOIP2017 提高组] 宝藏题目背景题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1样例 #2样例输入 #2样例输出 #2提示题解代码我的一些话[NOIP2017 提高组] 宝藏 题目背景 NOIP2017 D2T2 题目描…...

C#中实现串口通讯和网口通讯(使用SerialPort和Socket类)

仅作自己学习使用 1 准备部份 串口通讯需要两个调试软件commix和Virtual Serial Port Driver&#xff0c;分别用于监视串口和创造虚拟串口。网口通讯需要一个网口调试助手&#xff0c;网络上有很多资源&#xff0c;我在这里采用的是微软商店中的TCP/UDP网络调试助手&#xff0…...

LeetCode回溯算法的解题思路

回溯法概念 回溯法&#xff1a;一种通过探索所有可能的候选解来找出所有的解的算法。如果候选解被确认不是一个解&#xff08;或者至少不是最后一个解&#xff09;&#xff0c;回溯算法会通过在上一步进行一些变化抛弃该解&#xff0c;即回溯并且再次尝试。 应用场景 回溯算…...

泰克示波器(TBS2000系列)数学运算功能使用

目录 1 数学运算菜单1.1 运算符选择1.2 信源选择1.3 数学运算结果 1 数学运算菜单 Math运算按钮&#xff0c;用于实现对两个通道的信号进行实时的“加、减、乘”运算&#xff0c;计算时信源1在前面&#xff0c;信源2在运算符的右边&#xff0c;设置时设置信源与运算符就行了。…...

数据结构与算法之美学习笔记:50 | 索引:如何在海量数据中快速查找某个数据?

目录 前言为什么需要索引&#xff1f;索引的需求定义构建索引常用的数据结构有哪些&#xff1f;总结引申 前言 本节课程思维导图&#xff1a; 在第 48 节中&#xff0c;我们讲了 MySQL 数据库索引的实现原理。MySQL 底层依赖的是 B 树这种数据结构。留言里有同学问我&#xff…...

Python(SQLite)executescript用法

SQLite 数据库模块的游标对象还包含了一个 executescript() 方法&#xff0c;这不是一个标准的 API 方法&#xff0c;这意味着在其他数据库 API 模块中可能没有这个方法。但是这个方法却很实用&#xff0c;它可以执行一段 SQL 脚本。 例如&#xff0c;如下程序使用 executescr…...

BUUCTF-Real-[ThinkPHP]IN SQL INJECTION

目录 漏洞描述 漏洞分析 漏洞复现 漏洞描述 漏洞发现时间&#xff1a; 2018-09-04 CVE 参考&#xff1a;CVE-2018-16385 最高严重级别&#xff1a;低风险 受影响的系统&#xff1a;ThinkPHP < 5.1.23 漏洞描述&#xff1a; ThinkPHP是一款快速、兼容、简单的轻量级国产P…...

python安装步骤

安装 Python 的步骤如下&#xff1a; 在 Python 官方网站&#xff08;https://www.python.org&#xff09;上下载 Python 安装程序。运行下载的安装程序。在安装程序中选择要安装的 Python 版本&#xff08;通常选择最新版本&#xff09;&#xff0c;并选择安装目录。确保勾选…...

BlueLotus 下载安装使用

说明 蓝莲花平台BlueLotus&#xff0c;是清华大学曾经的蓝莲花战队搭建的平台&#xff0c;该平台用于接收xss返回数据。 正常执行反射型xss和存储型xss&#xff1a; 反射型在执行poc时&#xff0c;会直接在页面弹出执行注入的poc代码&#xff1b;存储型则是在将poc代码注入用…...

.[hudsonL@cock.li].mkp勒索病毒数据怎么处理|数据解密恢复

导言&#xff1a; 在当今数字化时代&#xff0c;勒索病毒已成为网络安全领域的一大威胁。其中一种新近出现的勒索病毒是由[hudsonLcock.li].mkp[hendersoncock.li].mkp[myersairmail.cc].mkp制作的&#xff0c;它以其高效的加密算法和勒索方式而备受关注。本文91数据恢复将介绍…...

基于SpringBoot和PostGIS的震中影响范围可视化实践

目录 前言 一、基础数据 1、地震基础信息 2、全国行政村 二、Java后台服务设计 1、实体类设计 2、Mapper类设计 3、控制器设计 三、前端展示 1、初始化图例 2、震中位置及影响范围标记 3、行政村点查询及标记 总结 前言 地震等自然灾害目前还是依然不能进行准确的预…...

JUnit实践教程——Java的单元测试框架

前言 大家好&#xff0c;我是chowley&#xff0c;最近在学单元测试框架——JUnit&#xff0c;写个博客记录一下&#xff01; 在软件开发中&#xff0c;单元测试是确保代码质量和稳定性的重要手段之一。JUnit作为Java领域最流行的单元测试框架&#xff0c;为开发人员提供了简单…...

选择大语言模型:2024 年开源 LLM 入门指南

作者&#xff1a;来自 Elastic Aditya Tripathi 如果说人工智能在 2023 年起飞&#xff0c;这绝对是轻描淡写的说法。数千种新的人工智能工具被推出&#xff0c;人工智能功能被添加到现有的应用程序中&#xff0c;好莱坞因对这项技术的担忧而戛然而止。 甚至还有一个人工智能工…...

ElastAlert 错误日志告警

文章目录 前言一、ElastAlert 概览1.1 简介1.2 ElastAlert 特性 二、ElastAlert 下载部署2.1 安装 Python3 环境2.2 下载 ElastAlert2.3 部署 ElastAlert 三、接入平台3.1 对外接口层3.2 服务层 前言 ElastAlert 是 Yelp 公司基于 python 开发的 ELK 日志告警插件&#xff0c;…...

day52 ResNet18 CBAM

在深度学习的旅程中&#xff0c;我们不断探索如何提升模型的性能。今天&#xff0c;我将分享我在 ResNet18 模型中插入 CBAM&#xff08;Convolutional Block Attention Module&#xff09;模块&#xff0c;并采用分阶段微调策略的实践过程。通过这个过程&#xff0c;我不仅提升…...

如何在看板中体现优先级变化

在看板中有效体现优先级变化的关键措施包括&#xff1a;采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中&#xff0c;设置任务排序规则尤其重要&#xff0c;因为它让看板视觉上直观地体…...

无法与IP建立连接,未能下载VSCode服务器

如题&#xff0c;在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈&#xff0c;发现是VSCode版本自动更新惹的祸&#xff01;&#xff01;&#xff01; 在VSCode的帮助->关于这里发现前几天VSCode自动更新了&#xff0c;我的版本号变成了1.100.3 才导致了远程连接出…...

基于当前项目通过npm包形式暴露公共组件

1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹&#xff0c;并新增内容 3.创建package文件夹...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)

笔记整理&#xff1a;刘治强&#xff0c;浙江大学硕士生&#xff0c;研究方向为知识图谱表示学习&#xff0c;大语言模型 论文链接&#xff1a;http://arxiv.org/abs/2407.16127 发表会议&#xff1a;ISWC 2024 1. 动机 传统的知识图谱补全&#xff08;KGC&#xff09;模型通过…...

【HTML-16】深入理解HTML中的块元素与行内元素

HTML元素根据其显示特性可以分为两大类&#xff1a;块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...

CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云

目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...

实现弹窗随键盘上移居中

实现弹窗随键盘上移的核心思路 在Android中&#xff0c;可以通过监听键盘的显示和隐藏事件&#xff0c;动态调整弹窗的位置。关键点在于获取键盘高度&#xff0c;并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

什么是Ansible Jinja2

理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具&#xff0c;可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板&#xff0c;允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板&#xff0c;并通…...

AI书签管理工具开发全记录(十九):嵌入资源处理

1.前言 &#x1f4dd; 在上一篇文章中&#xff0c;我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源&#xff0c;方便后续将资源打包到一个可执行文件中。 2.embed介绍 &#x1f3af; Go 1.16 引入了革命性的 embed 包&#xff0c;彻底改变了静态资源管理的…...