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

Linux c/c++服务器开发实践

在Linux C++开发环境中,通常有两种方式来开发多线程程序,一种是利用POSIX多线程 API函数来开发多线程程序,另外一种是利用C++自带线程类来开发程序。

常见的与线程相关的基本API函数:

API函数含义
pthread_create创建线程
pthread_exit线程终止自身执行
pthread_join等待一个线程的结束
pthread_self获取线程ID
pthread_cancel取消另外一个线程
pthread_kill向线程发送一个信号

pthread_join是个阻塞函数,函数pthread_join会让主线程挂起(即休眠,让出CPU),直到子线程都退出,同时pthread_join能让子线程所占的资源得到释放。子线程退出以,主线程会接收到系统的信号,从休眠中恢复。

线程的创建

下面举一个简单的创建线程的例子,新建一个createThreadTest.cpp 文件

例子1. 创建一个简单的线程,不传参数。

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>void *thfunc(void *arg){printf("in thfunc\n");return (void*)0;
}int main(int argc, char *argv[]){pthread_t tidp;int ret;ret = pthread_create(&tidp,NULL,thfunc,NULL); //创建线程if(ret){printf("pthread_create failed:%d\n",ret);return -1;}sleep(1); //main线程挂起1秒钟,为了让子线程有机会执行printf("in main:thread is created\n");return 0;
}

在终端执行:

g++ -o createThreadTest createThreadTest.cpp -lpthread
./createThreadTest.cpp 

输出:

in thfunc
in main:thread is created

在这个例子中,首先创建一个线程,线程函数在打印一行字符串后结束,而主线程在创建子线程后会等待一秒钟,避免主线程的过早结束而导致进程结束。如果没有等待函数sleep,则可能子线程的线程函数还没来得及执行,主线程就结束了。

例子2.创建一个线程,并传入整形参数。

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>void *thfunc(void *arg){int *pn = (int*)(arg);int n = *pn;printf("in thfunc n = %d\n", n);return (void*)0;
}int main(int argc, char *argv[]){pthread_t tidp;int ret, n = 110;ret = pthread_create(&tidp,NULL,thfunc,&n); //创建线程if(ret){printf("pthread_create failed:%d\n",ret);return -1;}pthread_join(tidp,nullptr);printf("in main:thread is created\n");return 0;
}

终端执行

g++ -o createThreadTest2 createThreadTest2.cpp -lpthread

输出如下:

in thfunc n = 110
in main:thread is created

例2和例1有两点不同,一是创建线程的时候,把一个整形变量的地址作为参数传给线程函数;二是等待子线程结束没有用sleep函数,而用pthread_join。sleep只是等待一个固定的时间,有可能在这个固定的时间内,子线程早已经结束,或者子线程运行的时间大于这个固定时间,因此用它来等待子线程结束并不精确;而用thread_join则会一直等到子线程结束后才会执行该函数后面的代码,我们可以看到它的第一个参数是子线程的ID。

例子3,创建一个线程,并传递字符串作为参数。

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>void *thfunc(void *arg){char *str;str = (char *)arg;printf("in thfunc str = %s\n", str);return (void*)0;
}int main(int argc, char *argv[]){pthread_t tidp;int ret;const char *str = "hello world";ret = pthread_create(&tidp,NULL,thfunc,(void *)str); //创建线程if(ret){printf("pthread_create failed:%d\n",ret);return -1;}pthread_join(tidp,nullptr);printf("in main:thread is created\n");return 0;
}
wjr@DESKTOP-UMB8379:~/compuThink$ g++ -o createThreadTest3 createThreadTest3.cpp -lpthread
wjr@DESKTOP-UMB8379:~/compuThink$ ./createThreadTest3
in thfunc str = hello world
in main:thread is created

例4.创建一个线程,并传递结构体作为参数。

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>typedef struct{ //定义结构体的类型int n;char *str;
}MYSTRUCT;void *thfunc(void *arg){MYSTRUCT *p = (MYSTRUCT*)arg;printf("in thfunc:n = %d, str = %s\n", p->n,p->str);return (void*)0;
}int main(int argc, char *argv[]){pthread_t tidp;int ret;MYSTRUCT mystruct;mystruct.n = 110;mystruct.str = "hello world";ret = pthread_create(&tidp,NULL,thfunc,(void *)&mystruct); //创建线程if(ret){printf("pthread_create failed:%d\n",ret);return -1;}pthread_join(tidp,nullptr);printf("in main:thread is created\n");return 0;
}
wjr@DESKTOP-UMB8379:~/compuThink$ g++ -o createThreadTest4 createThreadTest4.cpp -lpthread
createThreadTest4.cpp: In function ‘int main(int, char**)’:
createThreadTest4.cpp:21:20: warning: ISO C++ forbids converting a string constant to ‘char*’ [-Wwrite-strings]21 |     mystruct.str = "hello world";|                    ^~~~~~~~~~~~~
wjr@DESKTOP-UMB8379:~/compuThink$ ./createThreadTest4
in thfunc:n = 110, str = hello world
in main:thread is created

例5.创建一个线程,共享进程数据

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>int gn = 10; //定义一个全局变量,将会在主线程和子线程中用到void *thfunc(void *arg){gn++;   //递增1printf("in thfunc gn = %d\n", gn);return (void*)0;
}int main(int argc, char *argv[]){pthread_t tidp;int ret;ret = pthread_create(&tidp,NULL,thfunc,NULL); //创建线程if(ret){printf("pthread_create failed:%d\n",ret);return -1;}pthread_join(tidp,nullptr);     //等待子线程结束gn++;                           //子线程结束后,gn再递增1printf("in main gn=%d\n",gn);  //再次打印全局变量gn的值return 0;
}
wjr@DESKTOP-UMB8379:~/compuThink$ g++ -o createThreadTest5 createThreadTest5.cpp -lpthread
wjr@DESKTOP-UMB8379:~/compuThink$ ./createThreadTest5
in thfunc gn = 11
in main gn=12

从此例中可以看到,全局变量gn首先在子线程中递增1,等子线程结束后,再在主线程中递增1。两个线程都对同一个全局变量进行了访问。

###线程的属性
POSIX标准规定线程具有多个属性。线程的主要属性包括:分离状态(Detached State)、调度策略和参数(Scheduling Policy and Parameters)、作用域(Scope)、栈尺寸(Stack Size)、栈地址(Stack Address)、优先级(priority)等。Linux为线程属性定义一个联合体pthread_attr_t,注意是联合体而不是结构体,定义的地方在/usr/include/bits/pthreadtypes.h中,定义如下:

union pthread_attr_t
{char __size[__SIZEOF_PTHREAD_ATTR_T];long int __align;
}

从这个定义中可以看出,属性值都是存放在数组_size中的,不方便存取。但在Linux中有一组专门用于存取属性值的函数。如果获取线程的属性,首先要用函数pthread_getattr_np来获取属性结构体值,再用相应的函数来获取某个属性具体值。函数pthread_getattr_np声明如下:

int pthread_getattr_np(pthread_t thread, pthread_attr_t *attr);

其中参数thread是线程ID,attr返回线程属性结构体的内容。如果函数成功,返回0.否则返回错误码。注意,使用该函数需要在pthread.h钱定义宏_GNU_SOURCE,,代码如下

#define _GNU_SOURCE
#include <pthread.h>

并且,当函数pthread_getattr_np获得的属性结构体变量不再需要的时候,应该用函数pthread_attr_destroy进行销毁。
我们前面用pthread_create创建线程的时候,属性结构体指针参数用了NULL,此处创建的线程具有默认属性,即为非分离,大小为1MB的堆栈,与父进程具有同样级别的优先级。如果要创建非默认属性的线程,可以在创建线程之前用函数pthread_attr_init来初始化一个线程属性结构体,再调用相应的API函数来设置相应的属性。接着把属性结构体的指针作为参数传入pthread_create。函数pthread_attr_init声明如下:

int pthread_attr_init(pthread_attr_t *attr);

其中参数attr为指向线程属性结构体的指针。如果函数成功,返回0.否则返回一个错误码。
需要注意的是,使用pthread_attr_init初始化线程属性,使用完(即传入pthread_create)后需要使用pthread_attr_destroy进行销毁,从而释放相应资源。函数pthread_attr_destroy声明如下:

int pthread_attr_destroy(pthread_attr_t *attr);

1.分离状态

分离状态是线程的一个重要属性。POSIX线程的分离状态决定了一个线程以什么样的方式终止。默认的分离状态是可连接,即创建线程时如果使用默认属性,则分离状态属性就是可连接。
POSIX的线程要么是分离状态的,要么是非分离状态的(也称为可连接的,joinable)。前者用宏PTHREAD_CREATE_DETACHED表示,后者用宏PTHREAD_CREATE_JOINABLE表示。

默认情况下创建的线程是可连接的。

一个可连接的线程可以被其他线程收回资源和取消,并且它不会主动释放资源(比如栈空间),必须等待其他线程来回收其资源,因此我们要在主线程使用pthread_join,该函数是个阻塞函数,当它返回时,所等待的线程的资源也释放了。
再次强调,如果是可连接线程,当线程函数自己返回结束时或调用pthread_exit结束时,都不会释放线程所占用的堆栈和线程描述符(总计8K多),必须自己调用pthread_join且返回后,这些资源才会被释放。

这对于父进程长时间运行的线程来说,其结果会是灾难性的。因为父进程不退出并且没有调用pthread_join,则这些可连接线程的资源就一直不会释放,相当于僵尸线程了,僵尸线程越来越多,以后再想创建新线程将会变得没有资源可用。
如果不用pthread_join,即使父进程先于可连接子线程退出,也不会泄露资源。

如果父进程先于子线程退出,那么它将被init进程所收养,这个时候init就是它的父进程,它将调用wait系列函数为其回收资源,因此不会泄露资源。

总之,一个可连接的线程所占用的内容仅当有线程对其执行pthread_join才会释放,因此为了避免内存泄露,可连接的线程在终止时,要么已被设为DETACHED(可分离),要么使用pthread_join来回收其资源。另外,一个线程不能被多个线程等待,否则第一个受到信号的线程成功返回,其中调用pthread_join的线程将得到错误代码ESRCH。

了解了可连接线程,我们来看可分离的线程,这种线程运行结束时,其资源将立即被系统回收。可以这样理解,这种线程能独立(分离)出去,可以自生自灭,父线程不用管它了。将一个线程设置为可分离状态有两个方式,一种是调用函数pthread_detach,它可以将线程转换为可分离线程;另一种是在创建线程的时候将它设置为可分离状态。

基本过程是首先初始化一个线程属性的结构体变量(通过函数pthread_attr_init),然后将其设置为可分离状态(通过函数pthread_attr_setdetachstate),最后将该结构体变量的地址作为参数传入线程创建函数pthread_create,这样创建出来的线程就直接处于可分离状态。

函数pthread_attr_setdetachsate用于设置线程的可分离状态属性,声明如下:

int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);

例6.创建一个可分离线程。

#include <iostream>
#include <pthread.h>
#include <unistd.h>using namespace std;void *thfunc(void *arg){cout << ("sub thread is runnning\n");return NULL;
}int main(int argc, char *argv[]){pthread_t thread_id;pthread_attr_t thread_attr;struct sched_param thread_param;size_t stack_size;int res;res = pthread_attr_init(&thread_attr);if(res)cout << "pthread_attr_init failed:" << res << endl;res = pthread_attr_setdetachstate(&thread_attr,PTHREAD_CREATE_DETACHED);if(res)cout << "pthread_attr_setdetachstate failed:" << res << endl;res = pthread_create(&thread_id, &thread_attr, thfunc, NULL);if(res)cout << "pthread_create failed:" << res << endl;cout << "main thread will exit\n" << endl;sleep(1);return 0;
}
wjr@DESKTOP-UMB8379:~/compuThink$ g++ -o detachTest1 detachTest1.cpp -lpthread
wjr@DESKTOP-UMB8379:~/compuThink$ ./detachTest1 
main thread will exit
sub thread is runnning

在上面代码中,我们首先初始化了一个线程属性结构体,然后设置其分离状态为PTHREAD_CREATE_DETACHED,并用这个属性结构体作为参数传入线程创建函数中。这样,创建出来的线程就是可分离线程。这意味着,该线程结束时,它所占用的任何资源都可以立即让系统回收。程序的最后,我们让主线程挂起一秒,让子线程有机会执行。因为如果主线程很早就退出,将会导致整个进程很早退出,子线程就没有机会执行了。

如果子线程执行的时间长,则sleep的设置比较麻烦。有一种机制不用sleep函数即可让函数完整执行。对于可连接线程,主线程可以用pthread_join函数等待子线程结束。而对于可分离线程,并没有这样的函数,但可以采用这样的方法:
先让主线程退出而进程不退出,一直等待子线程退出了,主线程才退出,即在主线程中调用pthread_exit,在主线程如果调用了pthread_exit,那么终止的只是主线程,而进程的资源会为主线程创建的其他线程保持打开的状态,直到其他线程都终止。 值得注意的是,如果在非主线程(即其他子线程)中调用pthread_exit则不会有这样的效果,只会退出当前子线程。下面不用sleep函数,重新改写例6.

例7,创建一个可分离线程,且主线程先退出

#include <iostream>
#include <pthread.h>
#include <unistd.h>using namespace std;void *thfunc(void *arg){cout << ("sub thread is runnning\n");return NULL;
}int main(int argc, char *argv[]){pthread_t thread_id;pthread_attr_t thread_attr;struct sched_param thread_param;size_t stack_size;int res;res = pthread_attr_init(&thread_attr);if(res)cout << "pthread_attr_init failed:" << res << endl;res = pthread_attr_setdetachstate(&thread_attr,PTHREAD_CREATE_DETACHED);if(res)cout << "pthread_attr_setdetachstate failed:" << res << endl;res = pthread_create(&thread_id, &thread_attr, thfunc, NULL);if(res)cout << "pthread_create failed:" << res << endl;cout << "main thread will exit\n" << endl;pthread_exit(NULL); //主线程先退出,但进程不会此刻退出,下面的语句不会执行cout << "main thread has exited, this line will not run\n" << endl;return 0;
}
wjr@DESKTOP-UMB8379:~/compuThink$ g++ -o detachTest2 detachTest2.cpp -lpthread
wjr@DESKTOP-UMB8379:~/compuThink$ ./detachTest2
sub thread is runnning
main thread will exit

例8,获取线程的分离状态属性

相关文章:

Linux c/c++服务器开发实践

在Linux C开发环境中&#xff0c;通常有两种方式来开发多线程程序&#xff0c;一种是利用POSIX多线程 API函数来开发多线程程序&#xff0c;另外一种是利用C自带线程类来开发程序。 常见的与线程相关的基本API函数&#xff1a; API函数含义pthread_create创建线程pthread_exi…...

2023年11月在线IDE流行度最新排名

点击查看最新在线IDE流行度最新排名&#xff08;每月更新&#xff09; 2023年11月在线IDE流行度最新排名 TOP 在线IDE排名是通过分析在线ide名称在谷歌上被搜索的频率而创建的 在线IDE被搜索的次数越多&#xff0c;人们就会认为它越受欢迎。原始数据来自谷歌Trends 如果您相…...

视频批量剪辑:视频嵌套合并实战指南,剪辑高手速成秘籍

随着社交媒体的兴起&#xff0c;视频制作的需求越来越广泛。无论是个人用户还是专业团队&#xff0c;都需要对视频进行剪辑以符合其需求。而在这个过程中&#xff0c;批量剪辑视频的能力就变得至关重要。视频批量剪辑是指在一次操作中处理多个视频文件的剪辑。通过使用专业的视…...

每天一点python——day66

#每天一点Python——66 #字符串的分隔 #如图&#xff1a; #方法①split()从左开始分隔&#xff0c;默认空格为分割字符&#xff0c;返回值是一个列表 shello world jisuanji#首先创建一个字符串 list1s.split() print(list1)#输出结果是&#xff1a;[hello, world, jisuanji]注…...

搭建产品帮助中心其实很简单,方法都在这了!

网站帮助中心是一个为用户提供支持和解答问题的重要资源。它不仅可以提高用户体验&#xff0c;还能减少用户问题反馈的数量。通过提供清晰、易于理解的文档和指南&#xff0c;帮助中心可以帮助用户更好地了解产品或服务&#xff0c;并解决他们在使用过程中遇到的问题。接下来我…...

(离散数学)命题及命题的真值

答案&#xff1a; &#xff08;5&#xff09;不是命题&#xff0c;因为真值不止一个 &#xff08;6&#xff09;不是命题&#xff0c;因为不是陈述句 &#xff08;7&#xff09;不是命题&#xff0c;因为不是陈述句 &#xff08;8&#xff09;不是命题&#xff0c;真值不唯一...

计算机组成原理之处理器(流水线)

引言 为什么不采用单周期实现,硬件比较简单&#xff1f; 主要是因为效率太低&#xff0c;处理器中最长的路径&#xff08;一般是ld指令)决定了时钟周期 流水线概述 流水线是一种能使多条指令重叠执行的技术。 流水线更快的原因是所有的工作都在并行执行&#xff0c;所以单位…...

国际阿里云:云服务器灾备方案!!!

保障企业业务稳定、IT系统功能正常、数据安全十分重要&#xff0c;可以同时保障数据备份与系统、应用容灾的灾备解决方案应势而生&#xff0c;且发展迅速。ECS可使用快照、镜像进行备份。 灾备设计 快照备份 阿里云ECS可使用快照进行系统盘、数据盘的备份。目前&#xff0c;阿…...

计算机msvcp140.dll重新安装的四个解决方法,专门解决dll文件丢失问题的方法

在我多年的电脑使用经历中&#xff0c;曾经遇到过一个非常棘手的问题&#xff0c;那就是电脑提示找不到msvcp140.dll文件。这个问题让我苦恼了很久&#xff0c;但最终还是找到了解决方法。今天&#xff0c;我就来分享一下我解决这个问题的四种方法&#xff0c;希望对大家有所帮…...

提莫的idea的bug是真滴多

问题1&#xff1a;maven reload功能失效 我复制了一段代码到我项目里&#xff0c;这段代码依赖hutool包&#xff0c;于是我用idea快速导入&#xff0c;自动导入的是hutool-all:5.8.4。后来我发现这段还是有个函数报错&#xff0c;需要导入更高版本的hutool包才行&#xff0c;于…...

STM32笔记—EXTI外部中断

一、简介 中断&#xff1a;在主程序运行过程中&#xff0c;出现了特定的中断触发条件&#xff08;中断源&#xff09;&#xff0c;使得CPU暂停当前正在运行的程序&#xff0c;转而去处理中断程序&#xff0c;处理完成后又返回原来被暂停的位置继续运行&#xff1b; 中断优先级&…...

小程序分享当前页面

小程序分享页面的时候&#xff0c;大部分的资料都是显示的是onShareAppMessage 这个方法 /*** 用户点击右上角分享*/onShareAppMessage(res) {return {title: 您的好友向您分享了一本通讯录: this.data.setting.name,imageUrl: this.data.setting.share_img,path: pages/shar…...

10. GPIO中断

10. GPIO中断 回顾stm32中断系统STM32中断向量表中断向量偏移NVIC中断控制器 Cortex_A7 中断系统中断向量表GIC控制器中断IDGIC逻辑分块CP15协处理器c0寄存器c1寄存器c12寄存器c15寄存器 中断使能中断优先级设置优先级数配置 GICC_PMR抢占优先级和子优先级位数设置 GICC_BPR优先…...

【离散数学必刷题】谓词逻辑(第二章 左孝凌版)刷完包过!

专栏&#xff1a;离散数学必刷题 本章需要掌握的重要知识&#xff1a; 1.利用谓词表达式表示命题 2.变元的约束 3.谓词公式的定义、谓词公式的赋值 4.谓词公式的翻译&#xff08;注意在全总个体域时使用特性谓词&#xff09; 5.有限论域上量词的消去 6.谓词公式中关于量词的等价…...

SpringBoot系列-2 自动装配

背景&#xff1a; Spring提供了IOC机制&#xff0c;基于此我们可以通过XML或者注解配置&#xff0c;将三方件注册到IOC中。问题是每个三方件都需要经过手动导入依赖、配置属性、注册IOC&#xff0c;比较繁琐。 基于"约定优于配置"原则的自动装配机制为该问题提供了一…...

vue3+ts 前端实现打印功能

1.安装插件 npm install vue3-print-nb --save 2.全局引用 import { createApp } from ‘vue’ import App from ‘./App.vue’ import print from ‘vue3-print-nb’ const app createApp(App) app.use(print) app.mount(‘#app’) 例子 <template><div><el-…...

egg.js sequelize数据库操作配置

egg.js sequelize数据库操作配置 文章目录 egg.js sequelize数据库操作配置1. 数据库配置2. 迁移配置3.数据表设计和迁移4.模型创建 1. 数据库配置 安装并配置egg-sequelize插件&#xff08;它会辅助我们将定义好的 Model 对象加载到 app 和 ctx 上&#xff09;和mysql2模块&a…...

vagrant安装k8s集群

目录 概述前期准备安装virtualbox安装vagrant安装gitbash 集群架构集群安装集群初始化集群测试 概述 使用vagrant、virtualbox创建。 前期准备 安装virtualbox 访问官网安装&#xff0c;版本7.0.10 安装vagrant 访问官网安装&#xff0c;版本2.3.7 安装gitbash 访问官网…...

ArcGIS进阶:水源涵养功能分级评价操作

首先抛出水源涵养重要性评价的公式&#xff1a;水源涵养量降雨量-蒸散发量-地表径流量&#xff0c;其中地表径流量降雨量*平均地表径流系数 声明&#xff1a;以下数据来源于来自于牛强老师书籍&#xff08;城乡规划GIS技术&#xff09;。 以下给出重要性评价阈值表&#xff1…...

数据结构与算法 | 第四章:字符串

本文参考网课为 数据结构与算法 1 第四章字符串&#xff0c;主讲人 张铭 、王腾蛟 、赵海燕 、宋国杰 、邹磊 、黄群。 本文使用IDE为 Clion&#xff0c;开发环境 C14。 更新&#xff1a;2023 / 11 / 12 数据结构与算法 | 第四章&#xff1a;字符串 字符串概念字符串字符字符…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现

目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

质量体系的重要

质量体系是为确保产品、服务或过程质量满足规定要求&#xff0c;由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面&#xff1a; &#x1f3db;️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限&#xff0c;形成层级清晰的管理网络&#xf…...

macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用

文章目录 问题现象问题原因解决办法 问题现象 macOS启动台&#xff08;Launchpad&#xff09;多出来了&#xff1a;Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显&#xff0c;都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

在Ubuntu中设置开机自动运行(sudo)指令的指南

在Ubuntu系统中&#xff0c;有时需要在系统启动时自动执行某些命令&#xff0c;特别是需要 sudo权限的指令。为了实现这一功能&#xff0c;可以使用多种方法&#xff0c;包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法&#xff0c;并提供…...

【C语言练习】080. 使用C语言实现简单的数据库操作

080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...

用docker来安装部署freeswitch记录

今天刚才测试一个callcenter的项目&#xff0c;所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...

Map相关知识

数据结构 二叉树 二叉树&#xff0c;顾名思义&#xff0c;每个节点最多有两个“叉”&#xff0c;也就是两个子节点&#xff0c;分别是左子 节点和右子节点。不过&#xff0c;二叉树并不要求每个节点都有两个子节点&#xff0c;有的节点只 有左子节点&#xff0c;有的节点只有…...

Java多线程实现之Thread类深度解析

Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

算法岗面试经验分享-大模型篇

文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer &#xff08;1&#xff09;资源 论文&a…...

Java数值运算常见陷阱与规避方法

整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...