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

go_并发编程(1)

go并发编程

  • 一、 并发介绍
      • 1,进程和线程
      • 2,并发和并行
      • 3,协程和线程
      • 4,goroutine
  • 二、 Goroutine
    • 1,使用goroutine
      • 1)启动单个goroutine
      • 2)启动多个goroutine
    • 2,goroutine与线程
    • 3,goroutine调度
  • 三、runtime包
    • 1,runtime.Gosched()
    • 2,runtime.Goexit() 退出当时协程
    • 3, runtime.GOMAXPROCS
    • 4,将任务分配到不同的CPU逻辑核心上实现并行
    • 5,Go语言中的操作系统线程和goroutine的关系:
  • 四、channel
    • 1,CSP模型
    • 2,channel 类型
    • 3,创建channel
    • 4,channel 操作
      • 1)发送
      • 2)接收
      • 3)关闭
    • 5,无缓冲通道
    • 6,有缓冲通道
    • 7,从通道中遍历获取值
    • 8,单向通道
    • 8,通道总结

一、 并发介绍

1,进程和线程

  • A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。
  • B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。
  • C.一个进程可以创建和撤销多个线程;同一个进程中的多个线程之间可以并发执行

2,并发和并行

  • A. 多线程程序在一个核的cpu上运行,就是并发。
  • B. 多线程程序在多个核的cpu上运行,就是并行。

并发:
在这里插入图片描述
并行:
在这里插入图片描述

3,协程和线程

  • 协程:独立的栈空间,共享堆空间,调度由用户自己控制,本质上有点类似于用户级线程,这些用户级线程的调度也是自己实现的。
  • 线程:一个线程上可以跑多个协程,协程是轻量级的线程。

4,goroutine

  • goroutine 只是由官方实现的超级"线程池"。
    • 每个实力4~5KB的栈内存占用和由于实现机制而大幅减少的创建和销毁开销是go高并发的根本原因
  • goroutine 奉行通过通信来共享内存,而不是共享内存来通信。

二、 Goroutine

Go语言引入了goroutine机制,简化了并发编程。程序员只需定义任务函数,通过开启goroutine实现并发执行,而无需自己管理线程池、任务调度和上下文切换。
Go运行时负责智能分配任务到CPU,将复杂性隐藏在底层。
这使得Go成为现代化编程语言,使并发编程更加简单和高效。

1,使用goroutine

  • 在函数,或匿名函数 前面添加 go 关键词。
  • 一个goroutine必定对应一个函数,可以创建多个goroutine去执行相同的函数。

1)启动单个goroutine

func hello() {fmt.Println("Hello Goroutine!")
}
func main() {go  hello()fmt.Println("main goroutine done!")
}//但打印没有 ,Hello Goroutine! ,因为main 生命周期结束,goroutine 还没启动//让main 等等 goroutine 粗暴方法:func main() {go hello() // 启动另外一个goroutine去执行hello函数fmt.Println("main goroutine done!")time.Sleep(time.Second)
}

2)启动多个goroutine

  • 使用了sync.WaitGroup来实现goroutine的同步
var wg sync.WaitGroupfunc hello(i int) {defer wg.Done() // goroutine结束就登记-1fmt.Println("Hello Goroutine!", i)
}
func main() {for i := 0; i < 10; i++ {wg.Add(1) // 启动一个goroutine就登记+1go hello(i)}wg.Wait() // 等待所有登记的goroutine都结束
}打印出来,并不是顺序,因为这是因为10个goroutine是并发执行的,而goroutine的调度是随机的。

2,goroutine与线程

  • 可增长栈
    OS线程(操作系统线程)一般都有固定的栈内存(通常为2MB),一个goroutine的栈在其生命周期开始时只有很小的栈(典型情况下2KB),goroutine的栈不是固定的,他可以按需增大和缩小,goroutine的栈大小限制可以达到1GB,虽然极少会用到这个大。所以在Go语言中一次创建十万左右的goroutine也是可以的。

3,goroutine调度

Go语言的运行时(runtime)引入了GPM模型来实现并发调度,与传统操作系统调度不同。

  • G(goroutine): G是goroutine的缩写,代表一个任务单元。它存储了该任务的信息,以及与所在P(处理器)的关联。

  • P(处理器): P管理一组goroutine队列,包含当前goroutine的运行上下文。P负责调度自己的队列,比如暂停耗时长的任务、切换到其他任务。当P队列为空,它会从全局队列取任务,甚至从其他P队列抢占任务。

  • M(机器): M是Go运行时对操作系统内核线程的虚拟。通常是一一对应的关系,每个M执行一个goroutine。当一个G长时间阻塞在一个M上,会创建新的M,将其他G挂载在新M上。旧M释放后,用于回收资源。

  • GOMAXPROCS: 用于设定P的个数,控制并发度,但不会过多地增加P和M,以避免频繁切换的开销。

Go语言与其他语言不同之处在于,它在运行时实现了自己的调度器,使用m:n调度技术。这意味着goroutine的调度发生在用户态,避免了内核态与用户态的频繁切换,包括内存分配与释放都在用户态维护,性能开销较小。此外,Go语言充分利用多核硬件资源,将多个goroutine均匀分配在物理线程上,加上goroutine的轻量特性,保证了高效的并发调度性能。

三、runtime包

1,runtime.Gosched()

一种协作式多任务切换的方式,让正在运行的 goroutine 暂时停下来,让其他等待执行的 goroutine 有机会运行。

package mainimport ("fmt""runtime"
)func main() {go func(s string) {for i := 0; i < 5; i++ {fmt.Println(s)}}("world")// 主程for i := 0; i < 2; i++ {//切换 再次分配任务runtime.Gosched()fmt.Println("hello")}
}

2,runtime.Goexit() 退出当时协程

package mainimport ("fmt""runtime"
)func main() {go func() {defer fmt.Println("A.defer")func() {defer fmt.Println("B.defer")// 结束协程runtime.Goexit()defer fmt.Println("C.defer")fmt.Println("B")}()fmt.Println("A")}()for {}
}

3, runtime.GOMAXPROCS

  • Go运行时的调度器使用GOMAXPROCS参数来确定需要使用多少个OS线程来同时执行Go代码。默认值是机器上的CPU核心数。例如在一个8核心的机器上,调度器会把Go代码同时调度到8个OS线程上(GOMAXPROCS是m:n调度中的n)。

  • Go语言中可以通过runtime.GOMAXPROCS()函数设置当前程序并发时占用的CPU逻辑核心数。

  • Go1.5版本之前,默认使用的是单核心执行。Go1.5版本之后,默认使用全部的CPU逻辑核心数。

4,将任务分配到不同的CPU逻辑核心上实现并行

  • 单核心
package mainimport ("fmt""runtime""time"
)func a() {for i := 1; i < 10; i++ {fmt.Println("A:", i)}
}func b() {for i := 1; i < 10; i++ {fmt.Println("B:", i)}
}func main() {runtime.GOMAXPROCS(1)go a()go b()time.Sleep(time.Second)
}输出,看出是执行完一个goroutine ,再执行另一个
B: 1
B: 2
B: 3
B: 4
B: 5
B: 6
B: 7
B: 8
B: 9
A: 1
A: 2
A: 3
A: 4
A: 5
A: 6
A: 7
A: 8
A: 9
  • 多核心
package mainimport ("fmt""runtime""time"
)func a() {for i := 1; i < 10; i++ {fmt.Println("A:", i)}
}func b() {for i := 1; i < 10; i++ {fmt.Println("B:", i)}
}func main() {runtime.GOMAXPROCS(2)go a()go b()time.Sleep(time.Second)
}输出,看出是并发执行
B: 1
B: 2
B: 3
B: 4
B: 5
B: 6
B: 7
B: 8
A: 1
A: 2
A: 3
A: 4
A: 5
A: 6
A: 7
A: 8
A: 9
B: 9

5,Go语言中的操作系统线程和goroutine的关系:

  • 1.一个操作系统线程对应用户态多个goroutine。
  • 2.go程序可以同时使用多个操作系统线程。
  • 3.goroutine和OS线程是多对多的关系,即m:n

四、channel

1,CSP模型

并发执行函数的目的是让多个任务同时进行,但仅仅并发执行函数是不够的,因为这些函数可能需要相互交换数据。在并发环境中,共享内存虽然可以用于数据交换,但容易引发竞态问题,而使用互斥量会影响性能。

Go语言采用了CSP(Communicating Sequential Processes)并发模型,强调通过通信来共享数据,而不是通过共享数据来进行通信。这种方式更加安全且高效。

  • 关键点:

    • CSP模型: Go语言采用了CSP模型,强调通过通信来实现协程(goroutine)间的数据交换,而不是直接共享内存。

    • 通道(channel): 通道是用于协程间通信的一种机制,类似于一个队列,保证了数据的顺序性。通过在通道中发送和接收数据,协程可以安全地进行交互。

    • 通道的特点: 每个通道都有特定的元素类型,通道的操作遵循先进先出原则。通过通道的发送和接收操作,协程之间可以安全地进行数据交换,避免了竞态问题。

    • 并发优势: 通过通道,Go语言实现了安全且高效的并发编程,允许协程在不同任务之间进行数据交换,而不需要显式地使用互斥量进行加锁。

通过使用通道,Go语言的并发模型强调了协程之间通过通信共享数据,而不是通过共享数据来进行通信,从而避免了许多传统并发模型中常见的问题。这使得并发编程更加安全、简洁和高效。

2,channel 类型

  • channel 是一种类型,引用类型
声明格式:var 变量 chan 元素类型
例如:var ch1 chan int   // 声明一个传递整型的通道var ch2 chan bool  // 声明一个传递布尔型的通道var ch3 chan []int // 声明一个传递int切片的通道

3,创建channel

通道是引用类型,通道类型的空值是nil。var ch chan int
fmt.Println(ch) // <nil>声明的通道后需要使用make函数初始化之后才能使用。创建channel的格式如下:make(chan 元素类型, [缓冲大小])		// 缓冲大小可选例如:
ch4 := make(chan int)
ch5 := make(chan bool)
ch6 := make(chan []int)

4,channel 操作

1)发送

ch <- 10 // 把10发送到ch中

2)接收

x := <- ch // 从ch中接收值并赋值给变量x
<-ch       // 从ch中接收值,忽略结果

3)关闭

  close(ch)
  • 只有在通知接收方goroutine所有的数据都发送完毕的时候才需要关闭通道。
  • 通道是可以被垃圾回收机制回收的,它和关闭文件是不一样的,在结束操作之后关闭文件是必须要做的,但关闭通道不是必须的。

注意:

	1.对一个关闭的通道再发送值就会导致panic。2.对一个关闭的通道进行接收会一直获取值直到通道为空。3.对一个关闭的并且没有值的通道执行接收操作会得到对应类型的零值。4.关闭一个已经关闭的通道会导致panic。

5,无缓冲通道

func main() {ch := make(chan int)ch <- 10fmt.Println("发送成功")
}//出现以下错误fatal error: all goroutines are asleep - deadlock!goroutine 1 [chan send]:main.main().../src/github.com/pprof/studygo/day06/channel02/main.go:8 +0x54
  • 无缓冲的通道必须有接收才能发送。
  • 上面的代码会阻塞在ch <- 10这一行代码形成死锁

启动一个goroutine 解决该问题:

func recv(c chan int) {ret := <-cfmt.Println("接收成功", ret)
}
func main() {ch := make(chan int)go recv(ch) // 启用goroutine从通道接收值ch <- 10fmt.Println("发送成功")
}

无缓冲通道上的发送操作会阻塞,直到另一个goroutine在该通道上执行接收操作,这时值才能发送成功,两个goroutine将继续执行。相反,如果接收操作先执行,接收方的goroutine将阻塞,直到另一个goroutine在该通道上发送一个值。

使用无缓冲通道进行通信将导致发送和接收的goroutine同步化。因此,无缓冲通道也被称为同步通道。

6,有缓冲通道

func main() {ch := make(chan int, 1) // 创建一个容量为1的有缓冲区通道ch <- 10fmt.Println("发送成功")
}
  • 只要通道的容量大于零,那么该通道就是有缓冲的通道,通道的容量表示通道中能存放元素的数量。
  • 可以使用内置的len函数获取通道内元素的数量,使用cap函数获取通道的容量。

7,从通道中遍历获取值

package mainimport "fmt"func main() {ch1 := make(chan int)ch2 := make(chan int)// 开启goroutine 将 0~100 的数发到 ch1 中go func() {for i := 0; i < 100; i++ {ch1 <- i}close(ch1)}()// 开启goroutine 从ch1中接收值,发送给ch2go func() {for {i, ok := <-ch1if !ok {break}ch2 <- i * i}close(ch2)}()// 在主goroutine 打印ch2for i := range ch2 {fmt.Println("ch2:", i)}
}
  • 能从关闭通道中获取值
  • 通过遍历获取通道中(关闭的通道也行)的值

8,单向通道

func counter(out chan<- int) {for i := 0; i < 100; i++ {out <- i}close(out)
}func squarer(out chan<- int, in <-chan int) {for i := range in {out <- i * i}close(out)
}
func printer(in <-chan int) {for i := range in {fmt.Println(i)}
}func main() {ch1 := make(chan int)ch2 := make(chan int)go counter(ch1)go squarer(ch2, ch1)printer(ch2)
}
  • 1.chan<- int是一个只能发送的通道,可以发送但是不能接收;
  • 2.<-chan int是一个只能接收的通道,可以接收但是不能发送。

8,通道总结

在这里插入图片描述

相关文章:

go_并发编程(1)

go并发编程 一、 并发介绍1&#xff0c;进程和线程2&#xff0c;并发和并行3&#xff0c;协程和线程4&#xff0c;goroutine 二、 Goroutine1&#xff0c;使用goroutine1&#xff09;启动单个goroutine2&#xff09;启动多个goroutine 2&#xff0c;goroutine与线程3&#xff0…...

第一百一十五回 权限管理包permission_handler

文章目录 概念介绍使用方法示例代码经验分享 我们在上一章回中介绍了局部动态列表相关的内容&#xff0c;本章回中将介绍权限管理包 permission_hanadler.闲话休提&#xff0c;让我们一起Talk Flutter吧。 概念介绍 权限是使用某种功能的授权&#xff0c;比如使用手机上的相机…...

【机器学习】sklearn数据集的使用,数据集的获取和划分

「作者主页」&#xff1a;士别三日wyx 「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」&#xff1a;对网络安全感兴趣的小伙伴可以关注专栏《网络安全入门到精通》 sklearn数据集 二、安装sklearn二、获取数据集三、…...

Mysql之 optimizer_trace 相关总结

Mysql之 optimizer_trace 相关总结 MySQL官网介绍&#xff1a;https://dev.mysql.com/doc/dev/mysql-server/latest/PAGE_OPT_TRACE.html 1. 简介 MySQL优化器可以生成Explain执行计划&#xff0c;通过执行计划查看sql是否使用了索引&#xff0c;使用了哪种索&#xff1b; 但…...

【Linux命令详解 | wget命令】 wget命令用于从网络下载文件,支持HTTP、HTTPS和FTP协议

文章标题 简介一&#xff0c;参数列表二&#xff0c;使用介绍1. 基本文件下载2. 递归下载整个网站3. 限制下载速率4. 防止SSL证书校验5. 断点续传6. 指定保存目录7. 自定义保存文件名8. 增量下载9. 使用HTTP代理10. 后台下载 总结 简介 在编程世界中&#xff0c;处理网络资源是…...

DockePod信号处理机制与僵尸进程优化

Docke&Pod信号处理与僵尸进程优化 容器与信号的关系 SIGTERM信号&#xff1a;程序结束(terminate)信号&#xff0c;这是用来终止进程的标准信号&#xff0c;也是 kill 、 killall 、 pkill 命令所发送的默认信号。与SIGKILL不同的是该信号可以被阻塞和处理。通常用来要求程…...

NetApp StorageGRID 对象存储,使您能够跨公有、私有云和混合多云环境管理非结构化数据

NetApp StorageGRID 对象存储&#xff0c;使您能够跨公有、私有云和混合多云环境管理非结构化数据 主要优势 智能&#xff1a;了解行业领先的数据生命周期管理软件。 • 借助 NetApp StorageGRID 基于对象的存储解决方案的数据管理功能、您可以从大型非结构化数据中获得高价值…...

使用Java服务器实现UDP消息的发送和接收(多线程)

目录 简介&#xff1a;1. 导入必要的库2. 创建服务器端代码3. 创建客户端代码4. 实现多线程处理5. 测试运行示例代码&#xff1a;函数说明服务器端代码说明&#xff1a;客户端代码说明&#xff1a; 总结&#xff1a; 简介&#xff1a; 在本篇博客中&#xff0c;我们将介绍如何…...

Linux--查看端口占用情况

查看端口占用情况 在Linux使用过程中&#xff0c;需要了解当前系统开放了哪些端口&#xff0c;并且要查看开放这些端口的具体进程和用户&#xff0c;可以通过netstat命令进行简单查询 netstat命令各个参数说明如下&#xff1a;   -t : 指明显示TCP端口   -u : 指明显示UDP…...

微信小程序|自定义弹窗组件

目录 引言小程序的流行和重要性自定义弹出组件作为提升用户体验和界面交互的有效方式什么是自定义弹出组件自定义弹出组件的概念弹出层组件在小程序中的作用和优势为什么需要自定义弹出组件现有的标准弹窗组件的局限性自定义弹出组件在解决这些问题上的优势最佳实践和注意事...

【数据结构】实现顺序表

目录 一.介绍顺序表二.实现顺序表1.创建多文件2.顺序表的存储方式3.函数的声明4.初始化顺序表5.清理顺序表6.打印顺序表7.扩容8.尾插8.尾删9.头插10.头删11.查找12.修改13.在pos位置插入13.在pos位置删除 三.全部代码1.SeqList.h2.SeqList.c3.Test.c 一.介绍顺序表 顺序表是用…...

【嵌入式环境下linux内核及驱动学习笔记-(19)LCD驱动框架2-FrameBuffer】

目录 1、 Frmebuffer(帧缓冲&#xff09;操作介绍1.1 显示设备的抽象1.2 内存映像1.3 输出画面数据1.4 用户态下操作屏显1.4.1 用文件I / O 操作屏显1.4.2 mmap() 函数1.4.3 ioctl()函数1.4.5 用命令操作屏1.4.6 测试程序 2、Framebuffer总体框架2.1 框架要点2.2 fbmem.c分析2.…...

自己动手写数据库系统:实现一个小型SQL解释器(中)

我们接上节内容继续完成SQL解释器的代码解析工作。下面我们实现对update语句的解析&#xff0c;其语法如下&#xff1a; UpdateCmd -> INSERT | DELETE | MODIFY | CREATE Create -> CreateTable | CreateView | CreateIndex Insert -> INSERT INTO ID LEFT_PARAS Fie…...

HTML 与 XHTML 二者有什么区别

HTML 与 XHTML 二者有什么区别&#xff0c;你觉得应该使用哪一个并说出理由。 HTML 与 XHTML 之间的差别&#xff0c;主要分为功能上的差别和书写习惯的差别两方面。 关于功能上的差别&#xff0c;主要是 XHTML 可兼容各大浏览器、手机以及 PDA&#xff0c;并且浏览器也能快速正…...

fiddler抓包问题记录,支持https、解决 tunnel to 443

fiddler下载安装步骤及基本配置 fiddler抓包教程&#xff0c;如何抓取HTTPS请求&#xff0c;详细教程 可能遇到的问题及解决方案 1. 不能正常访问页面&#xff08;所有https都无法访问&#xff09; 解决方案&#xff1a;查看下面配置是否正确 Rules-customization 找到 OnB…...

Kubesphere中DevOps流水线无法部署/部署失败

摘要 总算能让devops运行以后&#xff0c;流水线却卡在了deploy这一步。碰到了两个比较大的问题&#xff0c;一个是无法使用k8sp自带的kubeconfig认证去部署&#xff1b;一个是部署好了以后但是没有办法解析镜像名。 版本信息 k8s&#xff1a;v1.21.5 k8sp&#xff1a;v3.3.…...

使用Nginx解决跨域问题

前言&#xff1a; 项目是公司的老项目&#xff0c;只有部署在服务器上的时候&#xff0c;项目才可以正常运行&#xff08;接口是通的&#xff09;&#xff1b;现在需求&#xff1a;在现有的项目代码上进行修改&#xff0c;请求接口是第三方给的。接口是正常的&#xff0c;通过A…...

在 OpenCV 中使用深度学习进行年龄检测-附源码

文末附完整源码和模型文件下载链接 在本教程中,我们将了解使用 OpenCV 创建年龄预测器和性别分类器项目的整个过程。 年龄检测 我们的目标是创建一个程序,使用图像来预测人的性别和年龄。但预测年龄可能并不像你想象的那么简单,为什么呢?您可能会认为年龄预测是一个回归问…...

【BASH】回顾与知识点梳理(三十一)

【BASH】回顾与知识点梳理 三十一 三十一. 进程的管理31.1 给进程发送讯号kill -signal PIDlinux系统后台常驻进程killall -signal 指令名称 31.2 关于进程的执行顺序Priority 与 Nice 值nice &#xff1a;新执行的指令即给予新的 nice 值renice &#xff1a;已存在进程的 nice…...

Linux 终端命令之文件浏览(3) less

Linux 文件浏览命令 cat, more, less, head, tail&#xff0c;此五个文件浏览类的命令皆为外部命令。 hannHannYang:~$ which cat /usr/bin/cat hannHannYang:~$ which more /usr/bin/more hannHannYang:~$ which less /usr/bin/less hannHannYang:~$ which head /usr/bin/he…...

线程同步:确保多线程程序的安全与高效!

全文目录&#xff1a; 开篇语前序前言第一部分&#xff1a;线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分&#xff1a;synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分&#xff…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序

一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...

React---day11

14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store&#xff1a; 我们在使用异步的时候理应是要使用中间件的&#xff0c;但是configureStore 已经自动集成了 redux-thunk&#xff0c;注意action里面要返回函数 import { configureS…...

LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》

这段 Python 代码是一个完整的 知识库数据库操作模块&#xff0c;用于对本地知识库系统中的知识库进行增删改查&#xff08;CRUD&#xff09;操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 &#x1f4d8; 一、整体功能概述 该模块…...

LLMs 系列实操科普(1)

写在前面&#xff1a; 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容&#xff0c;原视频时长 ~130 分钟&#xff0c;以实操演示主流的一些 LLMs 的使用&#xff0c;由于涉及到实操&#xff0c;实际上并不适合以文字整理&#xff0c;但还是决定尽量整理一份笔…...

pikachu靶场通关笔记19 SQL注入02-字符型注入(GET)

目录 一、SQL注入 二、字符型SQL注入 三、字符型注入与数字型注入 四、源码分析 五、渗透实战 1、渗透准备 2、SQL注入探测 &#xff08;1&#xff09;输入单引号 &#xff08;2&#xff09;万能注入语句 3、获取回显列orderby 4、获取数据库名database 5、获取表名…...

ubuntu22.04有线网络无法连接,图标也没了

今天突然无法有线网络无法连接任何设备&#xff0c;并且图标都没了 错误案例 往上一顿搜索&#xff0c;试了很多博客都不行&#xff0c;比如 Ubuntu22.04右上角网络图标消失 最后解决的办法 下载网卡驱动&#xff0c;重新安装 操作步骤 查看自己网卡的型号 lspci | gre…...

LLaMA-Factory 微调 Qwen2-VL 进行人脸情感识别(二)

在上一篇文章中,我们详细介绍了如何使用LLaMA-Factory框架对Qwen2-VL大模型进行微调,以实现人脸情感识别的功能。本篇文章将聚焦于微调完成后,如何调用这个模型进行人脸情感识别的具体代码实现,包括详细的步骤和注释。 模型调用步骤 环境准备:确保安装了必要的Python库。…...

如何配置一个sql server使得其它用户可以通过excel odbc获取数据

要让其他用户通过 Excel 使用 ODBC 连接到 SQL Server 获取数据&#xff0c;你需要完成以下配置步骤&#xff1a; ✅ 一、在 SQL Server 端配置&#xff08;服务器设置&#xff09; 1. 启用 TCP/IP 协议 打开 “SQL Server 配置管理器”。导航到&#xff1a;SQL Server 网络配…...

WEB3全栈开发——面试专业技能点P4数据库

一、mysql2 原生驱动及其连接机制 概念介绍 mysql2 是 Node.js 环境中广泛使用的 MySQL 客户端库&#xff0c;基于 mysql 库改进而来&#xff0c;具有更好的性能、Promise 支持、流式查询、二进制数据处理能力等。 主要特点&#xff1a; 支持 Promise / async-await&#xf…...