golang 基础案例_02

1.锁

        有时候我们的代码中可能会存在多个 goroutine 同时操作一个资源(临界区)的情况,这种情况下就会发生竞态问题(数据竞态)。

        (1)、互斥锁;(2)、读写互斥锁;(3)、sync.WaitGroup;(4)、sync.Once;(5)、sync.Map;(6)、atomic包

var (x       int64m       sync.Mutex // 互斥锁rwMutex sync.RWMutexmutex   sync.Mutex
)func Mutex() {wg.Add(2)go add()go add()wg.Wait()fmt.Println(x)
}func add() {for i := 0; i < 5000; i++ {m.Lock() // 修改x前加锁x = x + 1m.Unlock() // 改完解锁}wg.Done()
}func RWMutex() {/*读写锁分为两种:读锁和写锁。当一个 goroutine 获取到读锁之后,其他的 goroutine 如果是获取读锁会继续获得锁,如果是获取写锁就会等待;而当一个 goroutine 获取写锁之后,其他的 goroutine 无论是获取读锁还是写锁都会等待。*/// 使用互斥锁,10并发写,1000并发读do(writeWithLock, readWithLock, 10, 1000) // x:10 cost:1.466500951s// 使用读写互斥锁,10并发写,1000并发读do(writeWithRWLock, readWithRWLock, 10, 1000) // x:10 cost:117.207592ms/*从最终的执行结果可以看出,使用读写互斥锁在读多写少的场景下能够极大地提高程序的性能。不过需要注意的是如果一个程序中的读操作和写操作数量级差别不大,那么读写互斥锁的优势就发挥不出来。*/
}// writeWithLock 使用互斥锁的写操作
func writeWithLock() {mutex.Lock() // 加互斥锁x = x + 1time.Sleep(10 * time.Millisecond) // 假设读操作耗时10毫秒mutex.Unlock()                    // 解互斥锁wg.Done()
}// readWithLock 使用互斥锁的读操作
func readWithLock() {mutex.Lock()                 // 加互斥锁time.Sleep(time.Millisecond) // 假设读操作耗时1毫秒mutex.Unlock()               // 释放互斥锁wg.Done()
}// writeWithLock 使用读写互斥锁的写操作
func writeWithRWLock() {rwMutex.Lock() // 加写锁x = x + 1time.Sleep(10 * time.Millisecond) // 假设读操作耗时10毫秒rwMutex.Unlock()                  // 释放写锁wg.Done()
}// readWithRWLock 使用读写互斥锁的读操作
func readWithRWLock() {rwMutex.RLock()              // 加读锁time.Sleep(time.Millisecond) // 假设读操作耗时1毫秒rwMutex.RUnlock()            // 释放读锁wg.Done()
}func do(wf, rf func(), wc, rc int) {start := time.Now()// wc个并发写操作for i := 0; i < wc; i++ {wg.Add(1)go wf()}//  rc个并发读操作for i := 0; i < rc; i++ {wg.Add(1)go rf()}wg.Wait()cost := time.Since(start)fmt.Printf("x:%v cost:%v\n", x, cost)
}func WaitGroup() {/*WaitGroup在代码中生硬的使用time.Sleep肯定是不合适的,Go语言中可以使用sync.WaitGroup来实现并发任务的同步。 sync.WaitGroup有以下几个方法:方法名    功能func (wg * WaitGroup) Add(delta int)   计数器+delta(wg *WaitGroup) Done() 计数器-1(wg *WaitGroup) Wait() 阻塞直到计数器变为0sync.WaitGroup内部维护着一个计数器,计数器的值可以增加和减少。例如当我们启动了 N 个并发任务时,就将计数器值增加N。每个任务完成时通过调用 Done 方法将计数器减1。通过调用 Wait 来等待并发任务执行完,当计数器值为 0 时,表示所有并发任务已经完成。*/wg.Add(1)go hello()wg.Wait()fmt.Println("ni hao ya !!!")
}
func hello() {defer wg.Done()fmt.Println("hello world....")
}

2.SyncOnce

        在某些场景下我们需要确保某些操作即使在高并发的场景下也只会被执行一次,例如只加载一次配置文件等。
Go语言中的sync包中提供了一个针对只执行一次场景的解决方案——sync.Once,sync.Once只有一个Do方法,
延迟一个开销很大的初始化操作到真正用到它的时候再执行是一个很好的实践。因为预先初始化一个变量
(比如在init函数中完成初始化)会增加程序的启动耗时,而且有可能实际执行过程中这个变量没有用上,那么这个初始化操作就不是必须要做的。

func SyncOnce() {GetInstance() //并发安全的单例模式
}type singleton struct{}var instance *singleton
var once sync.Oncefunc GetInstance() *singleton {once.Do(func() {instance = &singleton{}})return instance
}

3.并发安全的map

        sync.Map 是 Go 语言中提供的一个并发安全的 map 类型,它是 Go 语言中 map 的替代品,它是并发安全的,并且它是通过引入 sync.RWMutex 来实现的。
在 Go 语言中,map 是无序的,而 sync.Map 是基于 sync.RWMutex 实现的,所以它是并发安全的。
sync.Map 的底层实现是通过哈希表来实现的,哈希表的底层实现是通过数组+链表来实现的,所以它是通过数组+链表来实现的,所以它是通过数组+链表来实现的,所以它是通过数组+链表来实现的,所以它是通过数组+链表来实现的,所以它是通过数组+链表来实现的,所以它是通过数组+链表来实现的,所以它是通过数组+链表来实现的,所以它是通过数组+链表来实现的,所以它是通过数组+链表来。


var smp = sync.Map{}func SyncMap() {wg := sync.WaitGroup{}// 对m执行20个并发的读写操作for i := 0; i < 20; i++ {wg.Add(1)go func(n int) {key := strconv.Itoa(n)smp.Store(key, n)         // 存储key-valuevalue, _ := smp.Load(key) // 根据key取值fmt.Printf("k=:%v,v:=%v\n", key, value)wg.Done()}(i)}wg.Wait()
}

4.atomic

        atomic包提供了底层的原子级内存操作,对于同步算法的实现很有用。这些函数必须谨慎地保证正确使用。
除了某些特殊的底层应用,使用通道或者 sync 包的函数/类型实现同步更好。
针对整数数据类型(int32、uint32、int64、uint64)我们还可以使用原子操作来保证并发安全,通常直接使用原子操作比使用锁操作效率更高。Go语言中原子操作由内置的标准库sync/atomic提供
func (o *TrinoQueryRunningExporter) AcquireLock() bool {
return atomic.CompareAndSwapInt64(&o.Lock, 0, 1)
}
func (o *TrinoQueryRunningExporter) ReleaseLock() {
atomic.CompareAndSwapInt64(&o.Lock, 1, 0)
}
这里的锁就是原子操作的使用案例。

func Atomic() {c1 := CommonCounter{} // 非并发安全test(c1)c2 := MutexCounter{} // 使用互斥锁实现并发安全test(&c2)c3 := AtomicCounter{} // 并发安全且比互斥锁效率更高test(&c3)
}type Counter interface {Inc()Load() int64
}// 普通版
type CommonCounter struct {counter int64
}func (c CommonCounter) Inc() {c.counter++
}func (c CommonCounter) Load() int64 {return c.counter
}// 互斥锁版
type MutexCounter struct {counter int64lock    sync.Mutex
}func (m *MutexCounter) Inc() {m.lock.Lock()defer m.lock.Unlock()m.counter++
}func (m *MutexCounter) Load() int64 {m.lock.Lock()defer m.lock.Unlock()return m.counter
}// 原子操作版
type AtomicCounter struct {counter int64
}func (a *AtomicCounter) Inc() {atomic.AddInt64(&a.counter, 1)
}func (a *AtomicCounter) Load() int64 {return atomic.LoadInt64(&a.counter)
}func test(c Counter) {var wg sync.WaitGroupstart := time.Now()for i := 0; i < 1000; i++ {wg.Add(1)go func() {c.Inc()wg.Done()}()}wg.Wait()end := time.Now()fmt.Println(c.Load(), end.Sub(start))
}

5.error

        Go 语言中把错误当成一种特殊的值来处理,不支持其他语言中使用try/catch捕获异常的方式
Go 语言中使用一个名为 error 接口来表示错误类型。
error 接口只包含一个方法——Error,这个函数需要返回一个描述错误信息的字符串。当一个函数或方法需要返回错误时,我们通常是把错误作为最后一个返回值。
我们可以根据需求自定义 error,最简单的方式是使用errors 包提供的New函数创建一个错误。
errors.New
当我们需要传入格式化的错误描述信息时,使用fmt.Errorf是个更好的选择。


func ErrorNew() {id := -1var err errorif id < 0 {err = errors.New("无效的id")fmt.Printf("id error:%v \n", err)}fmt.Println(fmt.Errorf("查询数据库失败,v err:%v \n", err))//但是上面的方式会丢失原有的错误类型,只拿到错误描述的文本信息。//为了不丢失函数调用的错误链,使用fmt.Errorf时搭配使用特殊的格式化动词%w,可以实现基于已有的错误再包装得到一个新的错误。fmt.Println(fmt.Errorf("查询数据库失败,w err:%w \n", err))//自定义结构体类型,可以自己定义结构体类型,实现 error`接口oper := &OpError{Op: "update",}fmt.Println(oper.Error())
}// Error OpError 类型实现error接口
func (e *OpError) Error() string {return fmt.Sprintf("无权执行%s操作", e.Op)
}// OpError 自定义结构体类型
type OpError struct {Op string
}

6.类型转换

        strconv包实现了基本数据类型与其字符串表示的转换,主要有以下常用函数: Atoi()、Itoa()、parse系列、format系列、append系列。
【扩展阅读】这是C语言遗留下的典故。C语言中没有string类型而是用字符数组(array)表示字符串,所以Itoa对很多C系的程序员很好理解。
strconv.Atoi  strconv.Itoa
Parse类函数用于转换字符串为给定类型的值:ParseBool()、ParseFloat()、ParseInt()、        ParseUint()。
Format系列函数实现了将给定类型数据格式化为string类型数据的功能

func TypeTransfer() {s1 := "100"i1, err := strconv.Atoi(s1) // Atoi()函数用于将字符串类型的整数转换为int类型if err != nil {fmt.Println("can't convert to int")} else {fmt.Printf("type:%T value:%#v\n", i1, i1) //type:int value:100}v := int64(-42)s10 := strconv.FormatInt(v, 10)fmt.Printf("%T, %v\n", s10, s10)s16 := strconv.FormatInt(v, 16)fmt.Printf("%T, %v\n", s16, s16)
}

7.ini

        在Go语言中,init()函数是一种特殊的函数,用于在程序启动时自动执行一次。它的存在为我们提供了一种机制,可以在程序启动时进行一些必要的初始化操作,为程序的正常运行做好准备
go语言中init函数用于包(package)的初始化,该函数是go语言的一个重要特性。
(1) init函数是用于程序执行前做包的初始化的函数,比如初始化包里的变量等
(2) 每个包可以拥有多个init函数
(3) 包的每个源文件也可以拥有多个init函数
(4) 同一个包中多个init函数的执行顺序go语言没有明确的定义(说明)
(5) 不同包的init函数按照包导入的依赖关系决定该初始化函数的执行顺序
(6) init函数不能被其他函数调用,而是在main函数执行之前,自动被调用
init函数和main函数的异同:
相同点:
两个函数在定义时不能有任何的参数和返回值,且Go程序自动调用。
不同点:
init可以应用于任意包中,且可以重复定义多个。
main函数只能用于main包中,且只能定义一个。
go中包的初始化顺序:
首先初始化包内声明的变量
之后调用 init 函数
最后调用 main 函数
两个函数的执行顺序:
对同一个go文件的init()调用顺序是从上到下的。
对同一个package中不同文件是按文件名字符串比较“从小到大”顺序调用各文件中的init()函数。
对于不同的package,如果不相互依赖的话,按照main包中"先import的后调用"的顺序调用其包中的init(),如果package存在依赖,则先调用最早被依赖的package中的init(),最后调用main函数。
如果init函数中使用了println()或者print()你会发现在执行过程中这两个不会按照你想象中的顺序执行。这两个函数官方只推荐在测试环境中使用,对于正式环境不要使用。
https://www.cnblogs.com/chenjiazhan/p/17473207.html
https://www.cnblogs.com/XiaoXiaoShuai-/p/14642055.html
init 函数的用途
(1) 初始化全局变量
(2) 执行一些必要的验证操作
注意:
init 函数不能被显式调用
init 函数只执行一次
避免在 init 函数中执行耗时操作


func Init() {fmt.Println("Init Test!!!")
}func init() {fmt.Println("hello world")
}

8.test

        Go语言中的测试依赖go test命令。编写测试代码和编写普通的Go代码过程是类似的,并不需要学习新的语法、规则或工具。
go test命令是一个按照一定约定和组织的测试代码的驱动程序。在包目录内,所有以_test.go为后缀名的源代码文件都是go test测试的一部分,不会被go build编译到最终的可执行文件中。
在*_test.go文件中有三种类型的函数,单元测试函数、基准测试函数和示例函数。
类型  格式 作用
测试函数    函数名前缀为Test 测试程序的一些逻辑行为是否正确
基准函数    函数名前缀为Benchmark    测试函数的性能
示例函数    函数名前缀为Example  为文档提供示例文档
go test命令会遍历所有的*_test.go文件中符合上述命名规则的函数,然后生成一个临时的main包用于调用相应的测试函数,然后构建并运行、报告测试结果,最后清理测试中生成的临时文件。

func TestChannel() {/*第一次循环时 i = 1,select 语句中包含两个 case 分支,此时由于通道中没有值可以接收,所以x := <-ch 这个 case 分支不满足,而ch <- i这个分支可以执行,会把1发送到通道中,结束本次 for 循环;第二次 for 循环时,i = 2,由于通道缓冲区已满,所以ch <- i这个分支不满足,而x := <-ch这个分支可以执行,从通道接收值1并赋值给变量 x ,所以会在终端打印出 1;后续的 for 循环以此类推会依次打印出3、5、7、9。*/ch := make(chan int, 1)for i := 1; i <= 10; i++ {select {case x := <-ch:fmt.Println(x)case ch <- i:}}
}

9.指针

        Go语言中的指针不能进行偏移和运算,因此Go语言中的指针操作非常简单,我们只需要记住两个符号:&(取地址)和*(根据地址取值)。


func Pointer() {a := 100b := &afmt.Printf("a:%v, b:%v,bp:%p \n", a, b, b)fmt.Printf("&b:%v,\n", &b)modValue(b)fmt.Printf("a:%v, b:%v,bp:%p \n", a, b, b)
}func modValue(i *int) {if i != nil {*i += 101}
}

10.context

 
func Context() {wg.Add(2)go func() {time.Sleep(time.Second * 2)fmt.Println("job 1 done")wg.Done()}()go func() {time.Sleep(time.Second * 1)fmt.Println("job 2 done")wg.Done()}()wg.Wait()fmt.Println("all job done")
}func Context2() {stop := make(chan bool)go func() {for {select {case <-stop:fmt.Println("got the stop channel")returndefault:fmt.Println("still working")time.Sleep(time.Second * 1)}}}()time.Sleep(time.Second * 5)fmt.Println("stop the goroutine")stop <- truetime.Sleep(time.Second * 5)
}func Context3() {ctx, cancel := context.WithCancel(context.Background())go worker(ctx, "worker1")go worker(ctx, "worker2")go worker(ctx, "worker3")time.Sleep(time.Second * 5)fmt.Println("stop the goroutine")cancel()time.Sleep(time.Second * 5)
}func worker(ctx context.Context, name string) {go func() {for {select {case <-ctx.Done():fmt.Println("got the stop channel")returndefault:fmt.Println(name, " still working")time.Sleep(time.Second * 1)}}}()
}func Context4() {// 创建一个带有取消功能的上下文ctx, cancel := context.WithCancel(context.Background())defer cancel()// 设置一个截止时间为5秒后ctx, cancel = context.WithDeadline(ctx, time.Now().Add(5*time.Second))defer cancel()// 向上下文中添加一个值ctx = context.WithValue(ctx, "key", "value")// 启动一个goroutine来监听上下文的取消信号go func() {select {case <-ctx.Done():fmt.Println("Context done:", ctx.Err())}}()// 启动一个goroutine来获取上下文中的值go func() {time.Sleep(2 * time.Second)value := ctx.Value("key")fmt.Println("Context value:", value)}()// 模拟一个耗时操作select {case <-time.After(10 * time.Second):fmt.Println("Operation completed")case <-ctx.Done():fmt.Println("Operation canceled due to context")}// 获取上下文的截止时间if deadline, ok := ctx.Deadline(); ok {fmt.Println("Context deadline:", deadline)} else {fmt.Println("No deadline set for context")}
}func ContextWaitGroup() {ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)defer cancel()var wg sync.WaitGroupwg.Add(1)go func() {defer wg.Done()for {select {case <-ctx.Done():fmt.Println("Goroutine canceled due to context")returndefault:// 模拟一些工作fmt.Println("Goroutine working...")time.Sleep(1 * time.Second)}}}()wg.Wait()fmt.Println("Main goroutine finished")
}func ContextWaitGroup1() {ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)defer cancel()go func() {for {select {case <-ctx.Done():fmt.Println("goroutine exiting...")returndefault:fmt.Println("goroutine working...")time.Sleep(500 * time.Millisecond)}}}()time.Sleep(3 * time.Second)fmt.Println("main function exiting...")
}

11.闭包

        (Closure)是一种特殊的函数,它可以捕获其创建时所在作用域中的变量。闭包通常与匿名函数一起使用,匿名函数可以访问并操作不在其参数列表中的外部变量。
Go语言中的闭包有几个特殊的用途和优势:
状态封装,控制变量生命周期,函数工厂,实现回调和延后执行,模块化和封装,实现接口,高阶函数,迭代器和生成器,避免命名冲突;
使用闭包的注意事项:内存泄漏,并发安全,循环引用.

func Closure() {/*这里这里start := time.Now() 已经运行了返回的函数再跟后面做减法*/defer TimeCost("test closure", 11)()time.Sleep(time.Second * 2)
}func TimeCost(handlerName string, req ...interface{}) func() {fmt.Printf(fmt.Sprintf("TimeCost for %s start now.", handlerName))start := time.Now()return func() {tc := time.Since(start)fmt.Printf(fmt.Sprintf("handle %s for request:%+v time cost is:%+v", handlerName, req, tc))}
}

12.http\UDP

                UDP协议(User Datagram Protocol)中文名称是用户数据报协议,是OSI(Open System Interconnection,开放式系统互联)参考模型中一种无连接的传输层协议,不需要建立连接就能直接进行数据发送和接收,属于不可靠的、没有时序的通信,但是UDP协议的实时性比较好,通常用于视频直播相关领域。

func process(conn net.Conn) {defer conn.Close()for {reader := bufio.NewReader(conn)var buf [128]byten, err := reader.Read(buf[:])if err != nil {fmt.Printf("read failed,err:%v", err)break}recv := string(buf[:n])fmt.Printf("接收到的数据:%v", recv)conn.Write([]byte("ok"))}
}func HttpServer() {listen, err := net.Listen("tcp", "127.0.0.1:8801")if err != nil {fmt.Printf("listen failed,err:%v", err)return}for {conn, err := listen.Accept()if err != nil {fmt.Printf("Accept failed ,err:%v", err)continue}go process(conn)}
}func HttpClinet() {conn, err := net.Dial("tcp", "127.0.0.1:8801")if err != nil {fmt.Printf("connect failed,err:%v", err)return}input := bufio.NewReader(os.Stdin)for {s, _ := input.ReadString('\n')s = strings.TrimSpace(s)if strings.ToUpper(s) == "Q" {return}//给服务端发消息_, err := conn.Write([]byte(s))if err != nil {fmt.Printf("send failed,err:%v \n", err)return}//从服务端接收消息var buf [1023]byten, err := conn.Read(buf[:])if err != nil {fmt.Printf("read failed,err:%v \n", err)return}fmt.Printf("收到服务单回复:%v", string(buf[:n]))}
}/*
RPC就是为了解决类似远程、跨内存空间、的函数/方法调用的。要实现RPC就需要解决以下三个问题。
如何确定要执行的函数?调用方和被调用方都需要维护一个{ function <-> ID }映射表,以确保调用正确的函数
如何表达参数? 参数或返回值需要在传输期间序列化并转换成字节流,反之亦然
如何进行网络传输?只要能够完成传输,调用方和被调用方就不受某个网络协议的限制
*/

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

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

相关文章

C++算法·前缀和

前缀和(Prefix(Prefix(Prefix Sum)Sum)Sum)的定义 前缀和是一种高效处理区间求和问题的算法技巧 其核心思想是通过预处理构建一个前缀和数组 使得后续的区间和查询可以在常数时间O(1)O(1)O(1)内完成 核心概念 定义 给定一个数组a[1...n]a[1...n]a[1...n],其前缀和数组s[1...…

JavaEE 初阶第十七期:文件 IO 的 “管道艺术”(下)

专栏&#xff1a;JavaEE初阶起飞计划 个人主页&#xff1a;手握风云 目录 一、Java文件内容写入 1.1. OutputStream 二、字符流读取和写入 2.1. Reader 2.2. Writer 三、示例练习 3.1. 查找文件功能 一、Java文件内容写入 1.1. OutputStream OutputStream同样只是⼀个抽…

【liunx】web高可用---nginx

NGINX简介Nginx&#xff08;发音为 “engine x”&#xff09;是一款由俄罗斯程序员 Igor Sysoev 开发的 轻量级、高性能的 HTTP 和反向代理服务器&#xff0c;同时也是一个 IMAP/POP3/SMTP 代理服务器。自 2004 年首次发布以来&#xff0c;Nginx 凭借其 高并发处理能力、低内存…

FPGA+护理:跨学科发展的探索(二)

FPGA护理&#xff1a;跨学科发展的探索&#xff08;二&#xff09; 系列文章目录 FPGA护理&#xff1a;跨学科发展的探索&#xff08;一&#xff09; 文章目录FPGA护理&#xff1a;跨学科发展的探索&#xff08;二&#xff09;系列文章目录引言三、FPGA 在精神医学护理中的应用…

localforage的数据仓库、实例、storeName和name的概念和区别

在 localForage 中&#xff0c;数据仓库、实例、storeName 和 name 是核心概念&#xff0c;用于管理底层存储&#xff08;IndexedDB/WebSQL/localStorage&#xff09;。以下是详细解释和区别&#xff1a; 1. 数据仓库 (Database) 定义&#xff1a;指底层的物理数据库&#xff…

使用MAS(Microsoft Activation Scripts)永久获得win10专业版和office全套

文章目录Microsoft Activation Scripts简介下载地址使用方法Microsoft Activation Scripts简介 MAS是Microsoft Activation Scripts缩写。 主要提供如下功能&#xff1a; 使用该脚本可以永久获得win10专业版和office全套&#xff08;可选&#xff09; 下载地址 https://pan…

零 shot 语义+在线闭环:深度学习让机器人学会“主动”

来gongzhonghao【图灵学术计算机论文辅导】&#xff0c;快速拿捏更多计算机SCI/CCF发文资讯&#xff5e;在当下&#xff0c;机器人与深度学习的融合正成为AI领域的核心发展趋势&#xff0c;相关研究在顶会顶刊上热度居高不下。从ICLR到CoRL&#xff0c;诸多前沿成果不断涌现&am…

Nginx学习笔记(三)——在 CentOS 7 中配置阿里云镜像源

&#x1f4da; Nginx学习笔记&#xff08;三&#xff09;——在 CentOS 7 中配置阿里云镜像源 在 CentOS 7 中配置阿里云镜像源可显著提升软件安装和更新的速度&#xff0c;以下是详细操作步骤&#xff1a; &#x1f527; 配置阿里云镜像源步骤 1️⃣ 备份原有源配置 sudo mv /…

WebSocket--简单介绍

一、什么是 WebSocket&#xff1f;定义&#xff1a;WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议。作用&#xff1a;实现客户端&#xff08;浏览器&#xff09;和服务器之间的实时、双向通信。优势&#xff1a;连接保持&#xff0c;通信实时性强&#xff08;不像 HT…

【STM32 LWIP配置】STM32H723ZG + Ethernet +LWIP 配置 cubemx

STM32H723ZG LAN8742 Ethernet LWIP 配置 cubemx &#x1f31e;这边记录一下这块mcu 配置以太网的过程&#xff0c;IDE是KEIL MDK&#xff0c;其实就是在下面多次提到的blog的基础上 在scatter file进行配置 首先&#xff0c;如果想要简单一点 直接去cubemx 那边获取相关的例…

EI检索-学术会议 | 人工智能、虚拟现实、可视化

第五届人工智能、虚拟现实与可视化国际学术会议&#xff08;AIVRV 2025&#xff09;定于2025年9月5-7日在中国 成都召开。人工智能正驱动各行业智能化转型&#xff0c;提升效率与质量&#xff1b;虚拟现实技术以其沉浸感重塑教育、娱乐、医疗等领域体验&#xff1b;可视化技术…

力扣(H指数)

一、题目分析 &#xff08;一&#xff09;问题描述 给定一个整数数组 citations&#xff0c;其中 citations[i] 表示研究者的第 i 篇论文被引用的次数。我们需要计算并返回该研究者的 H 指数。根据维基百科定义&#xff1a;H 指数代表“高引用次数”&#xff0c;一名科研人员的…

标准io(1)

标准I/O基础概念标准I/O&#xff08;Standard Input/Output&#xff09;是C语言提供的一组高级文件操作函数&#xff0c;位于<stdio.h>头文件中。与低级I/O&#xff08;如Unix的系统调用read/write&#xff09;相比&#xff0c;标准I/O引入了缓冲机制&#xff0c;能显著提…

线性代数1000题学习笔记

1000题线代基础第一章1-101000题线代基础第二章1-171000题线代基础第三章1-11

LeetCode算法日记 - Day 8: 串联所有单词的子串、最小覆盖子串

目录 1.串联所有单词的子串 1.2 解法 1.3 代码实现 2. 最小覆盖子串 2.1 题目解析 2.2 解法 2.3 代码实现 1.串联所有单词的子串 30. 串联所有单词的子串 - 力扣&#xff08;LeetCode&#xff09; 给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度…

linux实战:基于Ubuntu的专业相机

核心组件就是QTimerOpenCV的组合方案摄像头启停控制用QPushButton实现&#xff0c;帧显示必须用QLabel而不能用普通控件&#xff0c;视频流刷新用QTimer比多线程更简单想快速实现摄像头控制功能&#xff0c;核心组件就是QTimerOpenCV的组合方案。摄像头启停控制用QPushButton实…

《深度剖析前端框架中错误边界:异常处理的基石与进阶》

错误边界作为一种特殊的组件机制&#xff0c;正悄然重塑着应用应对异常的底层逻辑。它并非简单的代码片段组合&#xff0c;而是一套贯穿组件生命周期的防护体系&#xff0c;其核心价值在于将局部错误的影响牢牢锁定在可控范围内&#xff0c;避免整个应用陷入不可挽回的崩溃状态…

6GB显存玩转SD微调!LoRA-scripts本地部署教程,一键炼出专属AI画师

一、介绍LoRA-scripts&#xff08;又名 SD-Trainer&#xff09;&#xff0c;是一个专为训练低秩自适应&#xff08;LoRA&#xff09;模型设计的开源工具集主要应用于Stable Diffusion等AI绘图模型的微调&#xff0c;帮助用户高效创建定制化风格、角色或概念的轻量级模型。目前已…

探索AI的数学奇迹:Gemini 2.5 Pro如何摘得IMO金牌

🌍 引言:从人类天才到AI奇才的跨越 想象一下,一个AI模型坐在国际数学奥林匹克(IMO)的考场里,手里拿着笔(好吧,其实是处理token),面对那些让高中生们头疼不已的难题。它不是靠死记硬背,而是通过深思熟虑的推理,一步步攻克难关。这听起来像科幻小说,但2025年,这已…

MCP学习与实践

目录 1.MCP简介 1.1 MCP是什么 1.2 MCP与Agent关系&#xff1a; 1.3 MCP的架构 2. MCP原理 2.1 MCP 工作过程 2.2 MCP 通讯方式 2. MCP使用 2.1 cursor中增加MCP-SSE(高德地图MCP) 2.2 cursor中增加MCP-STDIO&#xff08;12306-MCP&#xff09; 本文详细讲解了什么是…