GO语言并发编程

news/2024/4/16 16:12:50/文章来源:https://blog.csdn.net/m0_72418211/article/details/136549953

点击名片关注 阿尘blog,一起学习,一起成长

1 Goroutine和channel

Go语言通过goroutine和channel来实现并发编程。Goroutine是Go语言中轻量级的线程,它由Go运行时(runtime)管理,并且拥有自己的栈空间。Goroutine的调度由Go运行时自动完成,不需要程序员手动创建和管理线程。

Channel是Go语言中用于在goroutine之间进行通信的机制,它类似于管道,用于在不同的goroutine之间传递数据。

下面是一个简单的Go语言并发编程案例,使用goroutine和channel实现两个goroutine之间的数据传递:

package mainimport ("fmt""time"
)// 生产者函数,向channel中发送数据
func producer(ch chan<- int) {for i := 0; i < 5; i++ {fmt.Printf("Producer produced: %d\n", i)ch <- i // 发送数据到channeltime.Sleep(time.Second) // 假设每次生产需要一些时间}close(ch) // 生产完毕后关闭channel
}// 消费者函数,从channel中接收数据
func consumer(ch <-chan int) {for value := range ch { // 从channel中接收数据,直到channel被关闭fmt.Printf("Consumer consumed: %d\n", value)time.Sleep(time.Second) // 假设每次消费需要一些时间}fmt.Println("Consumer finished.")
}func main() {ch := make(chan int) // 创建一个int类型的channel// 启动生产者goroutinego producer(ch)// 启动消费者goroutinego consumer(ch)// 主函数等待足够长的时间,以便观察并发执行的结果// 注意:在实际应用中,主函数通常不需要等待,除非有特定的同步需求time.Sleep(10 * time.Second)
}

注意事项:

不要传递nil的channel:尝试向nil的channel发送数据或从中接收数据都会导致panic。

关闭channel:一旦你不再需要向channel发送数据,就应该关闭它。接收方可以通过检查第二个返回值来判断channel是否关闭。如果channel已关闭并且没有更多的数据可以接收,则第二个返回值将为false。

避免死锁:确保所有的goroutine最终都能结束,避免产生死锁。这可能意味着你需要确保所有的goroutine都有退出的条件,或者使用sync.WaitGroup等待所有goroutine完成。

不要假设goroutine的执行顺序:goroutine的执行是由Go运行时调度的,因此你不能假设它们的执行顺序。

避免竞态条件:并发编程中常见的错误是竞态条件,即多个goroutine同时访问共享资源时可能导致数据不一致。使用互斥锁(sync.Mutex)或其他同步原语来保护共享资源。

注意性能:虽然goroutine轻量级,但创建大量的goroutine仍然可能对性能产生影响。合理地管理goroutine的数量和生命周期是很重要的。

使用select来处理多个channel:select语句允许你等待多个channel的操作。这对于实现超时、取消操作或同步多个goroutine之间的通信非常有用。

不要阻塞主goroutine:主goroutine(main函数)的结束会导致整个程序的结束。确保主goroutine不会无限制地等待其他goroutine,除非这是你的意图。

2 并发模式

  1. 工作者池(Worker Pool)

定义: 工作者池是一种并发模式,它创建了一个固定大小的Goroutine池来执行任务。当任务到达时,它会被分配给池中的一个空闲Goroutine。如果所有Goroutine都在工作,任务会被排队等待。

作用: 工作者池能够限制并发Goroutine的数量,避免因为创建过多的Goroutine而耗尽系统资源。

使用场景: 适用于需要处理大量并发任务,但又不希望无限制地增加Goroutine数量的场景。

示例代码:

package mainimport ("fmt""sync""time"
)func worker(id int, jobs <-chan int, results chan<- int, wg *sync.WaitGroup) {defer wg.Done()for j := range jobs {fmt.Printf("Worker %d started job %d\n", id, j)time.Sleep(time.Second) // 模拟耗时任务fmt.Printf("Worker %d finished job %d\n", id, j)results <- j * 2}
}func main() {numJobs := 10numWorkers := 3jobs := make(chan int, numJobs)results := make(chan int, numJobs)var wg sync.WaitGroup// 启动工作者for w := 1; w <= numWorkers; w++ {wg.Add(1)go worker(w, jobs, results, &wg)}// 分配任务for j := 1; j <= numJobs; j++ {jobs <- j}close(jobs) // 关闭jobs通道,表示不再发送新任务// 收集结果for a := 1; a <= numJobs; a++ {<-results}wg.Wait() // 等待所有工作者完成
}
  1. 生产者-消费者模型(Producer-Consumer)

定义: 生产者-消费者模型是一种并发设计模式,其中生产者负责生成数据并将其放入共享缓冲区,而消费者则从缓冲区中取出数据并处理它。

作用: 这种模型可以解耦生产数据和消费数据的速率,使得生产者和消费者可以独立运行,提高系统的吞吐量和响应能力。

使用场景: 适用于生产者和消费者速率不匹配的场景,如I/O密集型任务、计算密集型任务等。

示例在前面已提到。

  1. 流水线模型(Pipeline)

定义: 流水线模型是一种并发设计模式,它将一个大的任务分解成多个阶段,每个阶段由一个或多个Goroutine处理。每个阶段的输出作为下一个阶段的输入,形成一个流水线。

作用: 流水线模型可以提高任务的处理效率,通过将大任务拆分成多个小任务并并行处理,可以充分利用多核CPU的优势。

使用场景: 适用于需要将一个复杂任务拆分成多个简单任务并行处理的场景,如图像处理、数据转换等。

示例代码:

package mainimport ("fmt""sync"
)func main() {const numStages = 3const numJobs = 5// 创建阶段之间的通道channels := make([]chan int, numStages)for i := range channels {channels[i] = make(chan int)}// 最后的阶段不需要输出通道var wg sync.WaitGroupwg.Add(numStages)// 启动每个阶段的Goroutinefor i := 0; i < numStages; i++ {stage := igo func() {defer wg.Done()for j := 1; j <= numJobs; j++ {// 等待上一个阶段的输入input := <-channels[stage]// 处理输入并输出到下一个阶段output := process(input, stage)if stage == numStages-1 { // 如果是最后一个阶段,打印结果fmt.Printf("Stage %d: %d\n", stage, output)} else {channels[stage+1] <- output // 将输出发送到下一个阶段的通道}}}()}// 向第一个阶段的通道发送任务for j := 1; j <= numJobs; j++ {channels[0] <- j}close(channels[0]) // 关闭第一个阶段的通道,表示不再发送新任务// 等待所有阶段完成wg.Wait()// 关闭剩余的通道for i := 1; i < numStages; i++ {close(channels[i])}
}// process模拟每个阶段对输入的处理
func process(input, stage int) int {return input * (stage + 1)
}

这些并发模式在Go语言中都有广泛的应用,它们能够帮助开发者构建高效、可扩展的并发应用。通过选择适合的模式,开发者可以更加灵活地处理并发问题,提高程序的性能和响应能力

3 同步与互斥

在Go语言中,同步和互斥是确保多个Goroutines之间正确协作和避免数据竞争的关键机制。同步是协调Goroutines之间操作顺序的过程,而互斥则是确保在任意时刻只有一个Goroutine可以访问共享资源。

  1. 同步

定义:同步是协调多个Goroutines的执行顺序,确保它们按照预期的方式执行。

关键技术:

Channels:用于Goroutines之间的通信和同步。 WaitGroup:用于等待一组Goroutines的完成。

代码示例:使用Channels进行同步

package mainimport "fmt"func worker(id int, jobs <-chan int, results chan<- int) {for j := range jobs {fmt.Println("Worker", id, "started job", j)// 模拟工作for i := 0; i < 100000000; i++ {}fmt.Println("Worker", id, "finished job", j)results <- j * 2}
}func main() {numJobs := 5jobs := make(chan int, numJobs)results := make(chan int, numJobs)// 启动多个workerfor w := 1; w <= 3; w++ {go worker(w, jobs, results)}// 发送任务到jobs通道for j := 1; j <= numJobs; j++ {jobs <- j}close(jobs)// 从results通道接收结果并打印for a := 1; a <= numJobs; a++ {<-results}
}

作用:确保Goroutines按照预期的顺序执行任务,避免数据竞争。

注意事项:

不要在多个Goroutines之间直接共享内存,除非有适当的同步机制。 确保在不再发送数据到通道时关闭它,以便接收方知道何时结束循环。 2. 互斥

定义:互斥是确保在任意时刻只有一个Goroutine可以访问共享资源,以防止数据竞争(竞态条件)。

关键技术:

Mutex:用于保护共享资源不被多个Goroutines同时访问。

代码示例:使用Mutex进行互斥

package mainimport ("fmt""sync"
)var (counter intmutex   sync.Mutex
)func increment() {mutex.Lock()         // 在修改counter之前加锁counter++mutex.Unlock()       // 释放锁
}func main() {var wg sync.WaitGroupfor i := 0; i < 1000; i++ {wg.Add(1)go func() {defer wg.Done()increment()}()}wg.Wait()fmt.Println("Final Counter:", counter)
}

作用:确保在多个Goroutines访问共享资源时不会发生数据竞争,保证数据的一致性。

注意事项:

锁应该尽快释放,避免死锁。 避免在持有锁的情况下进行阻塞操作,如IO操作。 使用defer来确保锁总是被释放,即使在函数中出现错误或提前返回。

在Go中,使用Channels进行同步通常是首选方法,因为它们不仅用于同步,还可以用于Goroutines之间的通信。然而,在某些情况下,当需要更细粒度的控制或保护复杂的数据结构时,Mutexes可能是必要的。

4 内存管理

Go语言在内存管理方面有着出色的设计,这主要得益于它的垃圾回收机制、栈内存分配以及并发模型。在Go中,内存管理主要关注两个方面:如何分配和释放内存,以及如何安全地在并发环境中操作内存。

4.1 Go语言的内存机制

垃圾回收:Go语言使用垃圾回收器自动管理堆内存。当不再有任何变量引用某个对象时,垃圾回收器会自动回收其占用的内存。这避免了手动内存管理的复杂性和潜在的内存泄露问题。

栈内存分配:Go语言中的局部变量和函数调用的参数通常存储在栈内存中。栈内存的分配和释放由编译器自动管理,且效率非常高。当函数执行完成后,栈上的内存会被自动回收。

逃逸分析:编译器会分析代码以确定哪些变量应该分配在堆上(逃逸到堆),哪些变量应该保持在栈上。这有助于优化内存使用。

4.2 并发编程中的内存管理

并发编程中字面内存泄漏和有效管理内存是非常重要的,特别是

在Go语言中,避免内存泄漏和有效管理内存是非常重要的,特别是在处理大数据和长时间运行的项目时。以下是一些关于如何在Go语言中管理内存以避免内存泄漏的建议:

  1. 理解Go的垃圾回收器:Go语言使用了一个自动的垃圾回收器来自动回收不再使用的内存。了解垃圾回收器的工作原理和如何调整其性能可以帮助你更有效地管理内存。

  2. 避免全局变量和长生命周期的对象:全局变量和长生命周期的对象可能会导致内存占用持续增长。尽量将变量的作用域限制在需要的地方,避免不必要的全局变量。

    package mainimport ("fmt"
    )// 避免使用全局变量,而是传递必要的值作为函数参数
    func processData(data []int) {// 在函数内部处理数据,而不是使用全局变量result := make([]int, len(data))for i, v := range data {result[i] = v * 2}fmt.Println(result)
    }func main() {data := []int{1, 2, 3, 4, 5}processData(data) // 传递数据到函数,而不是使用全局变量
    }
  3. 使用指针和切片:在Go语言中,指针和切片是管理内存的有效工具。使用指针可以避免不必要的内存分配和复制,而切片则提供了动态数组的功能,可以根据需要增长和缩小。

    package mainimport ("fmt"
    )// 使用指针来避免不必要的内存分配和复制
    func modifySlice(s *[]int) {// 直接修改传入的切片,而不是创建一个新的切片for i := range *s {(*s)[i] *= 2}
    }func main() {data := []int{1, 2, 3, 4, 5}modifySlice(&data) // 传递切片的指针,以在函数内部修改它fmt.Println(data) // 输出修改后的切片
    }
  4. 使用defer来清理资源:defer语句用于在函数返回前执行一些清理工作,例如关闭文件、释放锁或数据库连接等。使用defer可以确保资源在不再需要时被正确释放,避免内存泄漏。

    package mainimport ("fmt""os"
    )func main() {file, err := os.Open("example.txt")if err != nil {fmt.Println("Error opening file:", err)return}defer file.Close() // 使用 defer 确保文件被关闭// 对文件进行操作...fmt.Println("File opened successfully")
    }
  5. 避免循环引用:循环引用是指两个或多个对象相互引用,导致它们无法被垃圾回收器回收。要避免这种情况,可以使用弱引用或确保在不再需要对象时解除引用关系。

    package mainimport "fmt"type A struct {B *B
    }type B struct {A *A
    }func main() {// 创建循环引用a := &A{}b := &B{A: a}a.B = b// 手动解除循环引用,以避免内存泄漏a.B = nilb.A = nil// 假设我们不再需要 a和b,现在它们可以被垃圾回收器回收了
    }
  6. 使用sync.Pool进行对象重用:sync.Pool是一个用于缓存和重用临时对象的池。在高并发的场景下,使用sync.Pool可以避免频繁的内存分配和垃圾回收,提高性能。

    package mainimport ("fmt""sync"
    )func main() {var pool = &sync.Pool{New: func() interface{} {return make([]int, 0, 5) // 初始化一个新的切片},}for i := 0; i < 10; i++ {s := pool.Get().([]int) // 从池中获取切片s = append(s, i)         // 使用切片pool.Put(s)              // 将切片放回池中,以便重用}// 注意:在实际使用中,将对象放回池中之前,需要确保对象处于可以被安全重用的状态
    }
  7. 监控和分析内存使用:使用工具如pprof来监控和分析你的程序的内存使用情况。这可以帮助你找到潜在的内存泄漏点,并进行优化。

以上建议,可以在Go语言中有效地管理内存,避免内存泄漏,并提高程序的性能和稳定性。

5 错误和处理

在Go语言中,错误处理和恢复是编程中非常重要的一部分,特别是在并发编程中。在Go中,错误通常通过返回值传递,而不是通过异常抛出。然而,Go也提供了panic和recover机制来处理严重的错误或异常情况。

首先,让我们讨论如何在Goroutine中处理错误。

Goroutine中的错误处理

在Goroutine中处理错误的基本策略是将错误返回给调用者。由于Goroutine是并发执行的,它们通常不会直接“抛出”错误到它们之外的上下文中。相反,它们应该通过通道(channel)将错误发送回主程序或其他协程以进行处理。

以下是一个示例,展示了如何在Goroutine中处理错误,并通过通道将错误发送回主程序:

package mainimport ("fmt""time"
)func doWork(errChan chan<- error, id int) {// 模拟工作,可能会产生错误if id%2 == 0 {fmt.Printf("Goroutine %d is working normally\n", id)} else {err := fmt.Errorf("Goroutine %d encountered an error", id)// 将错误发送到通道errChan <- err}
}func main() {// 创建一个用于接收错误的通道errChan := make(chan error)// 启动几个Goroutinefor i := 0; i < 5; i++ {go doWork(errChan, i)}// 等待所有Goroutine完成time.Sleep(1 * time.Second)// 关闭错误通道close(errChan)// 遍历错误通道,处理错误for err := range errChan {if err != nil {fmt.Println("Error:", err)}}
}

在这个例子中,doWork函数模拟了一个可能会产生错误的操作。如果发生错误,它会将错误发送到errChan通道。在main函数中,我们等待了一段时间以让Goroutines有时间执行,然后遍历errChan通道来处理任何收到的错误。

使用defer, panic和recover

panic和recover是Go中用于处理严重错误或异常情况的机制。panic用于引发一个非正常的程序流程中断,而recover用于捕获并处理panic。defer语句用于确保无论函数如何退出,都会执行某些清理操作。

以下是一个使用defer, panic和recover的例子:

package mainimport "fmt"func main() {defer func() {if r := recover(); r != nil {fmt.Println("Recovered in main", r)}}()doWork()
}func doWork() {defer func() {if r := recover(); r != nil {fmt.Println("Recovered in doWork", r)}}()fmt.Println("Starting doWork")panic("Something went wrong!")fmt.Println("Ending doWork") // 这行代码不会执行,因为panic发生了
}

在这个例子中,doWork函数中的panic调用会导致程序流程中断,并跳转到最近的defer语句。defer中的recover调用捕获了panic的值,并打印了一条恢复消息。由于doWork函数中的panic,所以紧随其后的fmt.Println("Ending doWork")不会执行。

注意,通常应该避免在常规错误处理中使用panic和recover,因为它们是为处理真正的程序错误和异常情况而设计的。在正常的错误处理中,应该优先使用错误返回值和错误处理模式。

6 性能优化建议

在Go语言中,通过并发来优化和提高程序的性能。以下是一些在Go语言中优化并发编程性能的建议:

1、使用Goroutines: Goroutines是Go语言中的轻量级线程,它们由Go运行时管理,并且非常便宜,可以创建成千上万个而不会给操作系统带来太大的负担。可尽量利用它们来处理并发任务。

2、避免阻塞: 阻塞操作(如I/O操作)会阻止goroutine继续执行,直到阻塞操作完成。你应该尽量减少阻塞操作,或者至少让它们不会阻塞整个程序。

// 使用带缓冲的channel来避免阻塞
ch := make(chan Item, bufferSize)
go func() {for item := range items {ch <- item}close(ch)
}()for item := range ch {processItem(item)
}

3、使用WaitGroup等待Goroutines完成: 当你启动多个goroutines并且需要等待它们全部完成时,使用sync.WaitGroup来同步。

4、选择合适的Channel大小: 带缓冲的channel可以缓解goroutines之间的同步压力,但过大的缓冲可能会隐藏程序中的逻辑错误。选择合适的缓冲区大小很重要。

5、避免过度创建Goroutines: 虽然goroutines是轻量级的,但过度创建它们也可能导致性能下降。使用工作池(worker pool)模式限制同时运行的goroutine数量。

6、使用Select避免阻塞: select语句允许goroutine等待多个channel操作,从而避免不必要的阻塞。

ch1 := make(chan Item)
ch2 := make(chan Item)go func() {// 生产者for item := range items {ch1 <- item}close(ch1)
}()go func() {// 另一个生产者for item := range otherItems {ch2 <- item}close(ch2)
}()for {select {case item1 := <-ch1:processItem(item1)case item2 := <-ch2:processOtherItem(item2)case <-time.After(timeout):// 超时处理return}
}

7、利用Goroutine本地存储: 使用sync.Local为goroutine提供本地存储,以减少在goroutine间传递数据的开销。

package mainimport ("fmt""sync""time"
)// 定义一个局部存储的键类型
type localKey int// 定义两个键
var key1 localKey = 1
var key2 localKey = 2func main() {// 创建一个Local对象localStore := sync.Local{}// 启动两个goroutinefor i := 0; i < 2; i++ {go func(id int) {// 在每个goroutine中存储一个值localStore.Store(key1, id)// 模拟一些工作time.Sleep(time.Second)// 从localStore中检索值if value, ok := localStore.Load(key1); ok {fmt.Printf("Goroutine %d retrieved value: %v\n", id, value)}// 清除存储的值localStore.Delete(key1)}(i)}// 等待goroutine完成time.Sleep(2 * time.Second)// 输出所有goroutine完成后的localStore状态fmt.Println("Local store after goroutines finished:", localStore)
}

8、避免不必要的内存分配: 在并发程序中,内存分配和垃圾回收可能会成为性能瓶颈。尽量重用对象,避免频繁的内存分配。

9、使用性能分析工具: 使用Go的性能分析工具(如pprof)来识别程序的瓶颈,并针对性地进行优化。

10、考虑使用并发安全的数据结构: Go标准库提供了一些并发安全的数据结构,如sync.Map,sync.Pool等,这些数据结构在并发环境下无需额外的锁操作,可以提高性能。

package mainimport ("fmt""sync"
)func main() {var wg sync.WaitGroupvar sm sync.Mapfor i := 0; i < 10; i++ {wg.Add(1)go func(id int) {defer wg.Done()sm.Store(fmt.Sprintf("counter-%d", id), 0)sm.Store(fmt.Sprintf("counter-%d", id), sm.LoadOrDefault(fmt.Sprintf("counter-%d", id), 0)+1)}(i)}wg.Wait()sm.Range(func(key, value interface{}) bool {fmt.Printf("%v: %v\n", key, value)return true})
}

记住,并发编程并不总是带来性能提升。在决定使用并发之前,首先要确定程序的瓶颈在哪里,并确保并发是解决这些瓶颈的有效方式。同时,不要过度优化,否则可能会引入额外的复杂性和维护成本。

7 高级特性:Context包

Context包:Context包是Go语言中用于管理Goroutine生命周期和取消操作的机制。通过Context,我们可以传递请求范围的值、取消信号和截止时间等信息,以便在并发操作中实现更加精细的控制。Context包在构建分布式系统、Web服务或长时间运行的后台任务时特别有用,它可以帮助我们避免资源泄漏和Goroutine泄漏等问题。

package mainimport ("context""fmt""time"
)func main() {// 创建一个带有超时功能的Contextctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)defer cancel() // 确保函数返回前调用cancel,避免资源泄漏go func() {// 模拟一个长时间运行的任务for {select {case <-ctx.Done():// 当Context被取消时,执行这里的代码fmt.Println("Task canceled")returndefault:// 继续执行任务逻辑fmt.Println("Task running...")time.Sleep(1 * time.Second)}}}()// 等待一段时间,然后取消Contexttime.Sleep(3 * time.Second)cancel()// 等待一段时间以确保Goroutine有时间响应取消信号time.Sleep(1 * time.Second)
}

在这个示例中,我们创建了一个带有2秒超时的Context对象。然后,我们启动了一个Goroutine来模拟一个长时间运行的任务。在这个Goroutine中,我们使用select语句来监听Context的Done()通道。当Context被取消时(例如,超时到达或显式调用cancel函数),Done()通道会关闭,并且Goroutine会收到通知并退出。主函数中等待一段时间后调用cancel()函数来取消Context,从而触发Goroutine的退出。

除了Context还有select以及sync.Map等等已在前面提到不在赘述,更多特性欢迎大家一起交流讨论。

扫描二维码关注阿尘blog,一起交流学习

d33ad746d91d528d78596a7250c66d63.png

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

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

相关文章

mac本地启动sentinel

启动Sentinel控制台 1&#xff09;下载sentinel控制台jar包 https://github.com/alibaba/Sentinel/releases/download/1.8.6/sentinel-dashboard-1.8.6.jar 2&#xff09;启动sentinel控制台 使用如下命令启动控制台&#xff1a; java -Dserver.port8080 -Dcsp.sentinel.d…

flink实战--Flink任务资源自动化优化

背景 在生产环境Flink任务资源是用户在实时平台端进行配置,用户本身对于实时任务具体配置多少资源经验较少,所以存在用户资源配置较多,但实际使用不到的情形。比如一个 Flink 任务实际上 4 个并发能够满足业务处理需求,结果用户配置了 16 个并发,这种情况会导致实时计算资…

Ajax+Axios+前后端分离+YApi+Vue-ElementUI组件+Vue路由+nginx【全详解】

目录 一.Ajax技术 二. Axios 三.前后台分离开发介绍 四. YAPI 五.前端工程化 六.vue工程的目录结构 七.Vue项目核心文件 八.Vue组件库ElementUI AboutView.vue最终代码 AboutView.vue最终代码 九.Vue路由 十.案例 十一.nginx介绍 一.Ajax技术 1.Ajax概述 Ajax: 全…

最新基于R语言lavaan结构方程模型(SEM)技术

原文链接&#xff1a;最新基于R语言lavaan结构方程模型&#xff08;SEM&#xff09;技术https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247596681&idx4&sn08753dd4d3e7bc492d750c0f06bba1b2&chksmfa823b6ecdf5b278ca0b94213391b5a222d1776743609cd3d14…

git分布式管理-头歌实验合并远程分支、rebase、储藏

一、合并远程分支merge 任务描述 在软件开发中&#xff0c;通常会在版本库中创建多个不同的分支进行开发。例如&#xff0c;最基本的可以有一个测试版分支和一个正式版分支&#xff0c;其中测试版分支用来完成最新功能代码的开发与测试&#xff0c;正式版则用于管理即将发布的版…

C# OpenCvSharp DNN 部署L2CS-Net人脸朝向估计

目录 介绍 效果 模型信息 项目 代码 下载 介绍 github地址&#xff1a;https://github.com/Ahmednull/L2CS-Net The official PyTorch implementation of L2CS-Net for gaze estimation and tracking 效果 模型信息 Inputs ------------------------- name&#xff1…

深入探索加载器(Loader)与插件(Plugin)的工作原理与技术实现

在软件开发和构建过程中&#xff0c;加载器与插件是两大核心组件&#xff0c;它们共同助力开发者实现代码的模块化、可扩展性以及复用性。对于技术型论坛的读者来说&#xff0c;理解这两者的工作原理和技术实现至关重要。以下&#xff0c;我们将对加载器和插件进行深入的剖析。…

【大数据】通过 docker-compose 快速部署 MinIO 保姆级教程

文章目录 一、概述二、MinIO 与 Ceph 对比1&#xff09;架构设计对比2&#xff09;数据一致性对比3&#xff09;部署和管理对比4&#xff09;生态系统和兼容性对比 三、前期准备1&#xff09;部署 docker2&#xff09;部署 docker-compose 四、创建网络五、MinIO 编排部署1&…

sheng的学习笔记-AI-多分类学习:ECOC,softmax

目录&#xff1a;sheng的学习笔记-AI目录-CSDN博客 基本术语&#xff1a; 若我们欲预测的是离散值&#xff0c;例如“好瓜”“坏瓜”&#xff0c;此类学习任务称为“分类”(classification)&#xff1b; 若欲预测的是连续值&#xff0c;例如西瓜成熟度0.95、0.37&#xff0c;…

Java基础数据结构之队列

一.什么是队列 队列是一种先进先出的数据结构&#xff0c;也就是从左边进从右边出&#xff0c;或者说&#xff0c;只允许在一端插入元素&#xff0c;在另一端删除元素 进行插入操作的一端称为队尾&#xff08;tail/rear&#xff09;&#xff0c;删除操作的一段称为队头&#…

第二门课:改善深层神经网络<超参数调试、正则化及优化>-优化算法

文章目录 1 Mini-batch梯度下降2 理解Mini-batch梯度下降法3 指数加权平均数4 理解指数加权平均数5 指数加权平均的偏差修正7 RMSprop<均方根传播>8 Adam优化算法<Momentum与RMSprop结合>9 学习率衰减10 局部最优的问题 1 Mini-batch梯度下降 Batch梯度下降法&…

win11本地账户登录密码忘了

第一个方法&#xff1a;没有权限&#xff08;可以研究下如何拿到权限&#xff0c;我后来没研究&#xff09; 第二个办法解决问题&#xff1a; 参考这个图&#xff1a; 步骤&#xff1a; 0.背景描述&#xff1a;我wly_yxx的账户&#xff08;类型是管理员&#xff09;知道pin可…

eclipse搭建java web项目

准备条件 eclipsejdk1.8 &#xff08;配置jdk环境&#xff09;apache-tomcat-8.5.97&#xff08;记住安装位置&#xff09; 一 点击完成 开始创建javaweb项目 import java.io.IOException; import java.io.PrintWriter;import javax.servlet.ServletException; import javax.s…

数据库系统概念(第一周)

⚽前言 &#x1f3d0;四个基本概念 一、数据 定义 种类 特点 二、数据库 三、数据库管理系统&#xff08;DBMS&#xff09; 四、 数据库系统&#xff08;DBS&#xff09; &#x1f3c0;数据库系统和文件系统对比 文件系统的弊端 &#x1f94e;数据视图 数据抽象 …

开源模型应用落地-工具使用篇-Spring AI-高阶用法(九)

一、前言 通过“开源模型应用落地-工具使用篇-Spring AI-Function Call&#xff08;八&#xff09;-CSDN博客”文章的学习&#xff0c;已经掌握了如何通过Spring AI集成OpenAI以及如何进行function call的调用&#xff0c;现在将进一步学习Spring AI更高阶的用法&#xff0c;如…

排序——选择排序

基本思想 每一趟在待排序元素中选取关键字最小的元素加入有序子序列。 算法代码 #include <iostream> using namespace std;//选择排序 void SelectSort(int nums[],int n){int i,j,min;for(i0;i<n-1;i){ //一共需要进行 n-1 趟 mini; //记录最小元素的下…

python导出数据到sqlite中

import sqlite3# 数据 data [{username: 张三, age: 33, score: 13},{username: 李四, age: 44, score: 14},{username: 王五, age: 55, score: 15}, ]# 连接SQLite数据库&#xff08;如果不存在则创建&#xff09; conn sqlite3.connect(test.db)# 创建游标对象 cursor con…

云服务器实例重启后,各个微服务的接口(涉及mysql操作的)都用不了了

问题描述&#xff1a; 云服务器被黑客植入挖矿。重启云服务器实例后得到解决&#xff0c;接着把docker&#xff08;zookeeper、redis啥的&#xff09;还有后端jar包啥的都重启了&#xff0c;然后发现后端接口访问不了&#xff0c;只有不涉及数据库操作的接口正常访问&#xff…

一篇论文回顾 Sora 文生视频技术的背景、技术和应用。

一篇论文回顾 Sora 文生视频技术的背景、技术和应用。 追赶 Sora&#xff0c;成为了很多科技公司当下阶段的新目标。研究者们好奇的是&#xff1a;Sora 是如何被 OpenAI 发掘出来的&#xff1f;未来又有哪些演进和应用方向&#xff1f; Sora 的技术报告披露了一些技术细节&…

【论文精读】融合知识图谱和语义匹配的医疗问答系统

&#x1f497;&#x1f497;&#x1f497;欢迎来到我的博客&#xff0c;你将找到有关如何使用技术解决问题的文章&#xff0c;也会找到某个技术的学习路线。无论你是何种职业&#xff0c;我都希望我的博客对你有所帮助。最后不要忘记订阅我的博客以获取最新文章&#xff0c;也欢…