Go学习(八):并发编程1

【Golang学习之旅】Go 语言基础语法概览 ✅ 适合人群: Golang 新手 | 后端开发者 | 面试求职者✅ 文章亮点: 语法详解 + 实战代码 + 高质量示例✅ 目标: 10 分钟快速掌握 Golang 基础语法,助你高效写项目!Go 语言(Golang)是Google开发的一门静态强类型并发支持强语法简洁的编程语言,广泛应用于后端开发云计算微服务区块链等领域。🔹 Go 语言的 4 大核心特点:✅ 语法简洁(减少代码冗余,开发高效)✅ 高性能(比 Python 快,比 Java 简单) 阅读详情

目录

1、概念:

进程和线程

并发和并行

协程和线程

2、goroutine

2.1 使用goroutine

2.2 可增长的栈

2.3 goroutine调度

3、runtime包Golang进程权限调度包runtimehttps://www.cnblogs.com/wt645631686/p/9656046.html

runtime.Gosched():让出CPU时间片,重新等待安排任务

runtime.Goexit():终止携程

runtime.GOMAXPROCS(),需要使用多少个OS线程来同时执行Go代码

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

4、Channel:函数与函数间交换数据

4.1 channel的基本使用:初始化,三种操作

4.2 两种方法在接收值的时候判断通道是否被关闭

4.3 单向通道(限制只能收or发)

 4.4 通道异常总结

 5、Goroutine池

6、定时器

1、概念:

进程和线程
A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。
B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。
C.一个进程可以创建和撤销多个线程;同一个进程中的多个线程之间可以并发执行。
并发和并行
A. 多线程程序在一个核的cpu上运行,就是并发。
B. 多线程程序在多个核的cpu上运行,就是并行。
协程和线程
协程:独立的栈空间,共享堆空间,调度由用户自己控制,本质上有点类似于用户级线程,这些用户级线程的调度也是自己实现的。
线程:一个线程上可以跑多个协程,协程是轻量级的线程。

2、goroutine

goroutine 高并发,奉行通过通信来共享内存,而不是共享内存来通信。

goroutine的概念类似于线程,但 goroutine是由Go的运行时(runtime)调度和管理的。Go程序会智能地将 goroutine 中的任务合理地分配给每个CPU。Go语言之所以被称为现代化的编程语言,就是因为它在语言层面已经内置了调度和上下文切换的机制。

把这个任务包装成一个函数,开启一个goroutine去执行这个函数就可以了

一个goroutine必定对应一个函数,可以创建多个goroutine去执行相同的函数。

2.1 使用goroutine

package main

import (
	"fmt"
	"sync"
	"time"
)

func hello() {
	fmt.Println("Hello Goroutine!")
}

// 启动多个goroutine
// 使用了sync.WaitGroup来实现goroutine的同步
var wg sync.WaitGroup

func hellosync(i int) {
	defer wg.Done() // goroutine结束就登记-1
	fmt.Println("Hello Goroutine!", i)
}

func main() {
	go hello() //// 启动另外一个goroutine去执行hello函数
	fmt.Println("main goroutine done!")
	//只会打印main goroutine done!
	//因为当main()函数结束时,goroutine就一同结束了,还来不及执行打印
	time.Sleep(time.Second)
	// main goroutine done!
	// Hello Goroutine!
	for i := 0; i < 10; i++ {
		wg.Add(1) // 启动一个goroutine就登记+1
		go hellosync(i)
	}
	wg.Wait() // 等待所有登记的goroutine都结束

}

主携程退出,则其他任务不执行

package main

import (
    "fmt"
    "time"
)

func main() {
    // 合起来写
    go func() {
        i := 0
        for {
            i++
            fmt.Printf("new goroutine: i = %d\n", i)
            time.Sleep(time.Second)
        }
    }()
    i := 0
    for {
        i++
        fmt.Printf("main goroutine: i = %d\n", i)
        time.Sleep(time.Second)
        if i == 2 {
            break
        }
    }
}
1.1.1. goroutin
main goroutine: i = 1
new goroutine: i = 1
new goroutine: i = 2
main goroutine: i = 2

2.2 可增长的栈

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

2.3 goroutine调度

GPM是Go语言运行时(runtime)层面的实现,是go语言自己实现的一套调度系统。区别于操作系统调度OS线程。

  • 1.G很好理解,就是goroutine,里面除了存放本goroutine信息外 还有与所在P的绑定等信息。
  • 2.Processor管理着一组goroutine队列,P里面会存储当前goroutine运行的上下文环境(函数指针,堆栈地址及地址边界),P会对自己管理的goroutine队列做一些调度(比如把占用CPU时间较长的goroutine暂停、运行后续的goroutine等等)当自己的队列消费完了就去全局队列里取,如果全局队列里也消费完了会去其他P的队列里抢任务。P的个数是通过runtime.GOMAXPROCS设定(最大256)
  • 3.M(machine)(thread)是Go运行时(runtime)对操作系统内核线程的虚拟, M与内核线程一般是一一映射的关系, 一个groutine最终是要放到M上执行的;

P与M一般也是一一对应的。他们关系是: P管理着一组G挂载在M上运行。当一个G长久阻塞在一个M上时,runtime会新建一个M,阻塞G所在的P会把其他的G 挂载在新建的M上。当旧的G阻塞完成或者认为其已经死掉时 回收旧的M。

 goroutine的优势:

  • Go运行时(runtime)自己的调度器调度的
  • goroutine的调度是在用户态下完成的, 不涉及内核态与用户态之间的频繁切换,包括内存的分配与释放,都是在用户态维护着一块大的内存池,充分利用多核优势

3、runtime包Golang进程权限调度包runtimeicon-default.png?t=N7T8https://www.cnblogs.com/wt645631686/p/9656046.html

runtime.Gosched():让出CPU时间片,重新等待安排任务

package main

import (
    "fmt"
    "runtime"
)

func main() {
    go func(s string) {
        for i := 0; i < 2; i++ {
            fmt.Println(s)
        }
    }("world")
    // 主协程
    for i := 0; i < 2; i++ {
        // 切一下,再次分配任务
        //主携程让出时间片,先执行了另一个
        runtime.Gosched()
        fmt.Println("hello")
    }
}

//world
//world
//hello
//hello

runtime.Goexit():终止携程

调用此函数会立即使当前的goroutine的运行终止(终止协程),而其它的goroutine并不会受此影响。

终止当前goroutine前会先执行此goroutine的还未执行的defer语句

不能再主函数调用runtime.Goexit,因为会引发panic

package main

import (
    "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 {
    }
}
//输出
//B.defer
//A.defer

runtime.GOMAXPROCS(),需要使用多少个OS线程来同时执行Go代码

默认值是机器上的CPU核心数,用来设置可以并行计算的CPU核数最大值,并返回之前的值。

package main

import (
	"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() {
	num := runtime.GOMAXPROCS(1)
	go a()
	go b()
	fmt.Println(num)  //输出了之前的值:8
	time.Sleep(time.Second)
}

//做完一个任务再做另一个任务,而且是B先,先进后出?
8
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 main

import (
	"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
A: 1
A: 2
A: 3
A: 4
A: 5
A: 6
A: 7
A: 8
A: 9
B: 2
B: 3
B: 4
B: 5
B: 6
Go语言中的操作系统线程和goroutine的关系:
  • 1.一个操作系统线程对应用户态多个goroutine。
  • 2.go程序可以同时使用多个操作系统线程。
  • 3.goroutine和OS线程是多对多的关系,即m:n。

4、Channel:函数与函数间交换数据

问题:可以使用共享内存进行数据交换,但是共享内存在不同的goroutine中容易发生竞态问题。为了保证数据交换的正确性,必须使用互斥量对内存进行加锁,这种做法势必造成性能问题。

Go语言的并发模型是CSP(Communicating Sequential Processes),提倡通过通信共享内存而不是通过共享内存而实现通信。

channel是可以让一个goroutine发送特定值到另一个goroutine的通信机制。

channel遵循先入先出(First In First Out)的规则,保证收发数据的顺序。每一个通道都是一个具体类型的导管,也就是声明channel的时候需要为其指定元素类型。

4.1 channel的基本使用:初始化,三种操作

package main

import (
	"fmt"
	"sync"
)

func recv(c chan int) {
	ret := <-c
	fmt.Println("接收成功", ret)
	close(c)
	fmt.Println("通道c关闭成功")
}
func main() {
	//1、创建channel
	var ch chan int
	//通道是引用类型,通道类型的空值是nil
	fmt.Println(ch) // <nil> 声明的通道后需要使用make函数初始化之后才能使用。
	//make函数初始化
	ch1 := make(chan int)
	fmt.Println(ch1) //0xc000060060
	go recv(ch1)     // 启用goroutine从通道接收值
	//2、三种操作
	//2.1 发送(send)
	ch1 <- 10 // 把10发送到ch中,能编译,但执行会出现deadlock错误
	//因为是无缓冲通道,必须要有人接收,会在这里死锁
	//解决办法1:启用一个goroutine去接收值,recv(ch)启动要在发送数据前,不然还是错

	fmt.Println("解决办法1发送成功")

	//解决办法2:使用 有缓冲通道
	ch2 := make(chan int, 1) // 创建一个容量为1的有缓冲区通道
	//可以使用内置的len函数获取通道内元素的数量,使用cap函数获取通道的容量,但少用
	ch2 <- 10
	fmt.Println("解决办法2发送成功")
	//2.2 接收(receive),ret := <-c
	//2.3 关闭(close) close(c)
	//如果你的管道不往里存值或者取值的时候一定记得关闭管道
	//close(ch1)	//报错了?why是因为接收方已经关闭了吗
	//fmt.Println("ch1关闭成功")
	close(ch2)
	fmt.Println("ch2关闭成功")
}
<nil>
0xc000060060
接收成功 10
通道c关闭成功
解决办法1发送成功
解决办法2发送成功
ch2关闭成功

4.2 两种方法在接收值的时候判断通道是否被关闭

package main

import (
	"fmt"
)

// channel 练习
// 有两种方式在接收值的时候判断通道是否被关闭,常用for range
func main() {
	ch1 := make(chan int)
	ch2 := make(chan int)
	// 开启goroutine将0~50的数发送到ch1中
	go func() {
		for i := 0; i < 50; i++ {
			ch1 <- i
		}
		close(ch1)
	}()
	// 开启goroutine从ch1中接收值,并将该值的平方发送到ch2中
	go func() {
		for {
			i, ok := <-ch1 // 方法1:通道关闭后再取值ok=false
			if !ok {
				break
			}
			ch2 <- i * i
		}
		close(ch2)
	}()
	// 在主goroutine中从ch2中接收值打印
	for i := range ch2 { // 方法2:通道关闭后会退出for range循环
		fmt.Println(i)
	}
}

4.3 单向通道(限制只能收or发)

1.chan<- int是一个只能发送的通道,可以发送但是不能接收;
2.<-chan int是一个只能接收的通道,可以接收但是不能发送。
3.双向通道可以转为单向通道,反之不行
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)
}

 4.4 通道异常总结

 5、Goroutine池

  • 本质上是生产者消费者模型
  • 可以有效控制goroutine数量,防止暴涨

例题:(还是有点不太明白为什么会deadlock)

  • 计算一个数字的各个位数之和,例如数字123,结果为1+2+3=6
  • 随机生成数字进行计算
package main

import (
	"fmt"
	"math/rand"
	"sync"
)

type Job struct {
	Id      int
	RandNum int
}
type Result struct {
	//这里必须传对象实例??
	job *Job
	sum int
}

// 使用等待组来等待所有协程完成
var wg sync.WaitGroup
var resultWg sync.WaitGroup

// 创建工作池
// 参数1:开几个协程
func createPool(num int, jobChan chan *Job, resultChan chan *Result) {
	for i := 0; i < num; i++ {
		wg.Add(1)
		go func(jobChan chan *Job, resultChan chan *Result) {
			defer wg.Done()
			//获取job管道的数据
			for job := range jobChan {
				r_num := job.RandNum
				//执行加操作
				var sum int
				for r_num != 0 {
					tmp := r_num % 10
					sum += tmp
					r_num /= 10
				}
				//得到结果
				r := &Result{job, sum}
				//将结果传入管道
				resultChan <- r
			}
		}(jobChan, resultChan)
	}
}

func main() {
	//生成随机数管道,需要记录job的id
	jobChan := make(chan *Job, 128) //有缓冲通道

	//计算结果管道
	resultChan := make(chan *Result, 128)

	//生成随机数,输入到管道
	for id := 0; id < 100; id++ {
		job := &Job{id, rand.Int()}
		jobChan <- job

	}

	//创建工作池
	createPool(16, jobChan, resultChan)

	// 关闭jobChan,告诉协程不会再有新的工作,没有这个会报All goroutines are asleep, deadlock
	close(jobChan)

	//打印协程
	resultWg.Add(1)
	go func(resultChan chan *Result) {
		defer resultWg.Done() // 打印协程完成后减少等待组计数

		for result := range resultChan {
			fmt.Printf("job id:%v randnum:%v result:%d\n", result.job.Id,
				result.job.RandNum, result.sum)
		}

	}(resultChan) //匿名函数传参

	// 等待所有工作协程完成
	wg.Wait()

	// 关闭 resultChan,告诉打印协程不再需要结果
	close(resultChan)

	// 等待打印协程完成
	resultWg.Wait()
}

如果你不关闭 jobChan,那么工作协程将一直等待从 jobChan 中接收任务,而主程序中也没有向 jobChan 中发送更多的任务,这将导致工作协程一直等待下去,最终出现死锁。因此,在使用有缓冲通道时,需要在合适的时机关闭通道以告知接收方没有更多的数据。

6、定时器

原理:传参是告诉 Timer 需要等待多长时间,Timer 是带有一个缓冲的 channal,在定时时间到达之前,没有数据写入 Timer.C,读取操作会阻塞当前协程,到达定时时间时,会向 channel 写入数据(当前时间),阻塞解除,被阻塞的协程得以恢复运行,达到延时或者定时执行的目的。Golang 定时器使用方法汇总-优快云博客

type Timer struct {
   C <-chan Time
   r runtimeTimer
}

6.1 Timer基本使用

package main

import (
	"fmt"
	"time"
)

//定时器使用

func main() {
	//1.timer基本使用

	t1 := time.Now()
	fmt.Println("now time1: ", t1.Format("2006-01-02 15:04:05"))
	t2 := time.NewTimer(2 * time.Second)
	t3 := <-t2.C //// 超时时间还没到时,协程会阻塞;超时时间到了之后会返回当前时间
	fmt.Println("now time2: ", time.Now().Format("2006-01-02 15:04:05"))
	fmt.Println("now time3: ", t3.Format("2006-01-02 15:04:05"))
	// now time1:  2023-11-01 21:50:04
	// now time2:  2023-11-01 21:50:06
	// now time3:  2023-11-01 21:50:06

	//2、timer.After
	//After() 函数接受一个时长 d,然后 After() 等待 d 时长,等待时间到后,将等待完成时所处时间点写入到 channel 中并返回这个只读 channel。
	//NewTimer(d).C 和after 每次都是 return 了一个新的对象,已踩坑
	t4 := time.After(time.Second * 2)
	fmt.Println("now time: ", time.Now().Format("2006-01-02 15:04:05"))
	time.Sleep(time.Second * 4)
	chant := <-t4
	fmt.Println("now time: ", chant.Format("2006-01-02 15:04:05"))
	fmt.Println("now time: ", time.Now().Format("2006-01-02 15:04:05"))
	// now time:  2023-11-01 21:42:08
	// now time:  2023-11-01 21:42:10
	// now time:  2023-11-01 21:42:12

	//3、time.Reset()
	//<-t2.C	使用会报错,即timer只能响应1次,想要重新使用就需要reset
	//如果调用 time.Reset() 和 time.Stop() 时,timer 已过期或者已停止,则会返回 false
	fmt.Println("now time: ", time.Now().Format("2006-01-02 15:04:05"))
	t5 := time.NewTimer(4 * time.Second)
	//ok := t2.Reset(2 * time.Second) //返回false
	ok := t5.Reset(2 * time.Second)
	fmt.Println("ok: ", ok)
	<-t5.C
	fmt.Println("now time: ", time.Now().Format("2006-01-02 15:04:05"))
	// now time:  2023-11-01 21:52:09
	// ok:  true
	// now time:  2023-11-01 21:52:11

	//4、time.Stop()
	fmt.Println("now time: ", time.Now().Format("2006-01-02 15:04:05"))
	t6 := time.NewTimer(4 * time.Second)
	t6.Stop() //停止定时器
	fmt.Println("now time: ", time.Now().Format("2006-01-02 15:04:05"))

}

6.2 Ticker

Ticker 是一个周期触发定时的定时器,按给定时间间隔往 channel 发送系统当前时间,而 channel 的接收者可以以固定的时间间隔从 channel 中读取

package main
import (
	"fmt"
	"time"
)
func main() {
	for {
		select {
		case <-time.Tick(2 * time.Second):
			fmt.Println("2 second over:", time.Now().Second())
		case <-time.After(7 * time.Second):
			fmt.Println("5 second over, timeover", time.Now().Second())
			return
		}
	}
}
start second: 14
1 second over: 15
1 second over: 16
1 second over: 17
1 second over: 18
1 second over: 19
1 second over: 20
1 second over: 21
7 second over: 21

package main

import (
    "fmt"
    "time"
)

func main() {
    //创建定时器,每隔1秒后,定时器就会给channel发送一个事件(当前时间)
    ticker := time.NewTicker(time.Second * 1)

    i := 0
    go func() {
        for { //循环
            <-ticker.C
            i++
            fmt.Println("i = ", i)

            if i == 5 {
                ticker.Stop() //停止定时器
            }
        }
    }() //别忘了()

    //死循环,特地不让main goroutine结束
    for {
    }
}

Go 学习笔记 · 进阶篇 · 第二天:并发编程深度实践 Go 的并发是轻量高效的,Goroutine 比线程香。Channel 是 goroutine 之间的传声筒。WaitGroup、Mutex、Context 是并发控制的三剑客。并发编程容易埋雷,写完一定记得加-race运行!今日咒语:“人生苦短,让 goroutine 去忙,主程序才轻松。 阅读详情

相关推荐

76、Go并发编程实战:从基础到实战案例解析

本文通过实战案例,深入浅出地介绍了Go语言的并发编程特性。从基础的goroutine和channel,到生产者消费者模型,再到Web服务器和并发爬虫,以及并发排序,全方位展示了Go在并发编程方面的优势和应用。同时,文中还提到了一些实用技巧和最佳实践,帮助读者更好地理解和应用Go的并发编程。

silenceallat的博客 1847

100天精通Golang(基础入门篇)——第1天:学习Go语言基本概念

100天精通Golang(基础入门篇)——第1天:学习Go语言基本概念,欢迎来到100天精通Golang的基础入门篇!在这个系列教程中,我们将带领你逐步掌握Go语言的核心概念和语法。本篇博文将着重介绍Go语言的基本概念,包括其定义、用途和优势。通过阅读本文,你将对Go语言有一个清晰的了解。在第1天的学习中,我们将探索Go语言的基本概念。我们将介绍Go语言是什么,它可以用来做什么,以及它相较于其他编程语言的优势。

猫头虎技术团队:授渔优于赠鱼,兴趣引领智慧,探索之乐尤显珍贵。商务合作+:Libin9iOak ,万粉变现+:优快云WF,猫头虎承诺每年免费为100名C站创作者做账号流量诊断服务!全网搜:猫头虎技术团队,点击文章底部名片或直接私信我一切皆可谈,快找虎哥! 6万+

GO语言后端学习路线

在学习 Go 后端开发之前,需要掌握 Go 语言的语法和基础知识。

Xiao651的博客 2261

Golang的学习路线

Golang的学习路线

Mr_XiMu的博客 9500

Golang go routine调度器:让并发编程更简单高效

Golang(Go)的并发模型以goroutine和channel为核心,而调度器作为goroutine运行的底层引擎,直接影响程序的并发性能和资源利用率。解析调度器的核心架构(GMP模型)和调度策略揭示调度器如何实现轻量级线程的高效管理演示调度器在实际项目中的应用和优化方法覆盖内容包括调度器原理、算法实现、数学建模、实战案例和工具推荐,适用于希望深入理解Go并发机制的开发者。背景介绍:定义目标、读者和术语核心概念:解析GMP模型及调度器架构算法原理:工作窃取、抢占调度等核心算法数学模型。

AI Agent Harness Engineering 1003

Go语言从入门到精通:一站式学习指南

Go语言是由Google的Rob Pike、Ken Thompson和Robert Griesemer设计的编程语言。Go语言旨在提供一种简洁、快速、并且易于并发编程的解决方案。Go语言的设计理念之一是“简单就是美”,它抛弃了传统编程语言中的一些复杂特性,如继承、多态等,而通过一些现代特性(如goroutine并发机制)弥补这些限制。Go语言以其简单、并发、性能优异的特点,成为了开发者的首选语言之一。通过本文的介绍,从Go的基础语法到进阶应用,你应该对Go有了一个全面的了解。如果。

weixin_52088134的博客 2880

Go-Golang学习总结笔记

资源 某hub会审核不通过,自己去找吧 书籍 网站 基础 go官方中文网 菜鸟教程 C语言网 go语言中文网 golang系列 视频 基础 安装 依赖管理 数据类型 变量和常量 流程控制 函数 包 面向对象编程 提高 ...

关注网络安全、云原生安全 2万+

VSCode搭配Keil开发STM32:从环境配置到代码跳转全流程(避坑指南)

本文详细介绍了如何使用VSCode搭配Keil进行STM32开发,从环境配置到代码跳转的全流程指南。通过安装必备插件和正确配置项目文件,开发者可以在VSCode中享受现代编辑器的便捷功能,同时保留Keil的编译调试能力,提升嵌入式开发效率。

weixin_28832121的博客 253

从Java到Go:Java工程师的Golang学习指南

作为一名Java工程师,你可能已经习惯了Java的面向对象编程、强大的生态系统以及丰富的工具链。然而,随着Go语言(Golang)在云计算、微服务和并发编程领域的崛起,学习Go语言成为了一个非常有价值的选择。本文将通过对比Java和Go语言的方式,帮助你快速掌握Go语言的核心概念,并通过详细的案例讲解,帮助你理解两者之间的异同。 Go 对比: Go 对比: Go 对比: Go 对比:Java通过类和接口实现多线程编程,或者使用包中的高级并发工具。 Go Go语言通过实现并发,是轻量级的线程,由Go运

SunnyRivers 1750

路由工具之策略路由policy-based-routing、MQC三板斧流分类、流行为、流策略

PBR(Policy-Based Routing,策略路由)策略路由可理解为控制路由,因为PBR策略的优先级高于传统路由表的转发,所以它主要用于影响数据包转发的技术。

Hades_Ling的优快云博客 5580

2025东三省D题深圳杯数学建模实战解析:基于多模型融合的法医STR混合样本智能识别系统

本文深入解析了2025年东三省数学建模竞赛D题,即深圳杯赛题,聚焦于法医STR混合样本的智能识别挑战。文章系统阐述了构建一个基于多模型融合的智能识别系统的完整流程,涵盖贡献者人数判定、DNA贡献比例估计、个体基因型推断以及STR谱图去噪四大核心步骤。通过融合高斯混合模型、随机森林、非负矩阵分解、小波变换与深度学习等多种技术,并强调特征工程的重要性,为破解法医DNA混合样本分析这一行业难题提供了清晰的数学建模与AI实战思路。

weixin_29050271的博客 390

【golang学习之旅】Go 语言并发编程(goroutine、channel 的使用)

✅ 适合人群:Golang 开发者 | 并发编程初学者 | 后端开发工程师✅ 文章亮点:深入解析 Goroutine & Channel + 代码示例 + 实战场景✅ 目标:掌握 Go 并发编程核心,让你的代码更高效、更强大!Goroutine是Go语言的轻量级线程,由Go运行时管理,而非操作系统。相比传统线程,Goroutine占用内存小:默认仅2KB栈空间,可动态扩展调度高效:Go运行时管理Goroutine的调度,无需手动创建线程启动成本低:创建Goroutine比创建线程的开销小很多。

程序员林北北的博客 1252

Palantir的本体论建模跟数仓建模的异同

Palantir的Foundry Ontology通过构建组织的数字孪生体,将数据集和模型映射为对象类型、属性、链接和操作类型,形成完整的语义知识网络。与传统数仓建模相比,本体建模以业务问题为驱动,强调细粒度语义和实时决策能力,采用图语义技术实现灵活的映射关系。

轩哥的个人研究和成长记录 1589

golang学习之八:并发编程基础(goroutine、channel、select)

这里写目录标题协程goroutine创建协程主协程退出,其它协程也都退出主协程先退出导致其它子协程未来的及调用runtime包Gosched(写在哪个协程里,哪个协程就让出cpu时间片)Goexit(写在哪个协程里,就终止哪个协程执行)GOMAXPROCS(设置可并行计算的CPU核数的最大值,并返回之前的值) 协程goroutine goroutine是Go并行设计的核心。goroutine说到底其实就是协程,但是它比线程更小,十几个goroutine可能体现在底层就是五六个线程,Go语言内部帮你实现了这些

java_xxxx的博客 1918

CIMPro孪大师,支持 3DGS 高斯泼溅的一站式零代码数字孪生平台

高斯泼溅导入数字孪生开发引擎

2501_90821319的博客 1109

深度解读扫地机器人的导航原理

科技改变生活,不可否认科技的进步的确为我们的生活带来许多便捷,以智能家电为例,智能电饭煲、智能洗碗机、智能扫地机等产品已逐渐成为家庭的一分子,其中以扫地机表现最为抢眼。能做到智能规划清扫,收割了不少家庭主妇及科技爱好者的芳心。但扫地机并非一开始就这么智能,最初的扫地机只能做到随机式清扫,会频繁出现碰撞现象。 近年来,通过业界的不断努力,目前,扫地机器人已告别乱碰乱撞现象,能做到自主路径...

qq_38403231的博客 6503

golang学习九:Go并发编程

一、概述: 所谓并发编程是指在一台处理器上“同时”处理多个任务; 宏观的并发是指在一段时间内, 有多个程序在同时运行; 并发在微观上, 是指在同一时刻只能有一条指令执行, 但多个程序指令被快速的轮换执行, 使得在宏观上具有多个进程同时执行的效果, 但在微观上并不是同时执行的, 只是把时间分成若干段, 使多个程序快速交替的执行; 1. 并行与并发: 并行(parallel): 指在同一时刻, 有多条指令在多个处理器上同时执行; 并发(concurrency)L指在同一时刻只能有一条指令执行, 但多个进程指

浅弋、璃鱼的博客 903

山景BP10_128DBG开发板ADC_Key驱动

最近在学习山景BP10_128DBG开发板,想使用开发板上的按键。看了看原理图和之前用过的按键好像有点不一样。常见的按键都是判断IO口是否被拉低或拉高来判断按键是否被按下。山景BP10_128DBG开发板上的按键是ADC按键,每个按键按下之后ADC的采样值是不同的,通过ADC采样回来的值进而判断哪个按键按下。这种方式核心思想就是用一个ADC通道来节约IO口,对于IO紧张的芯片是一个不错的解决思路。

weixin_56646002的博客 1308

Golang基础7-并发编程

Golang基础-并发编程

m0_61973596的博客 1550

SAP_通用模块-MASS批量操作技巧

SAP_MM模块-MASS/MM17批量更新主数据详细教程

风沙的博客 2167

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

Golang如何简化并发编程?

Go语言通过其独特的并发模型和简洁的语法大大简化了并发编程的复杂性。通过使用轻量级的goroutines和直观的channels通信机制,开发者可以更加高效地处理并发任务,从而提高应用的性能和响应速度。然而,在使用Go语言的并发特性时,也需要注意一些关键点,如避免死锁、控制goroutine的数量以及优雅地关闭goroutines等。通过遵循这些注意事项和最佳实践,开发者可以更加高效地利用Go语言的并发优势来构建高性能、高可用的应用。来自:33066.cn/gonglue/163.html。

PrimoRosal的博客 1799

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

为什么劝你要学习Golang以及GO语言(Go语言知识普及)

为什么劝你要学习Golang以及GO语言

m0_69234258的博客 4973

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

Golang 生态学习

虽然 Go 在人工智能领域不如 Python 那样流行,但由于其出色的性能和并发支持,Go 也开始被用于一些 AI 和机器学习(ML)项目,尤其是对于需要高并发、高性能的任务,Go 可以提供优势。• 《Go语言圣经》(The Go Programming Language):这是学习 Go 语言的经典书籍,涵盖了 Go 的所有基础内容。它被广泛应用于供应链、金融等行业的区块链解决方案。:Go 的并发模型(goroutines 和 channels)是 Go 的强项,学习并发编程是使用 Go 的重要步骤。

A_Tevens的博客 1515

Golang深度学习

在2009年,Google公司发布了一种新的编程语言,名为Go(或称为Golang),旨在提高编程效率、简化并发编程,并提供强大的标准库支持。Go语言的设计者们希望通过Go语言能够解决软件开发中的一些长期存在的问题,比如并发编程的复杂性、垃圾回收机制的效率以及跨平台的兼容性等。自从2009年发布以来,Go语言已经经历了多个版本的迭代和改进,持续吸引着全球开发者的兴趣和使用。其简洁性、高效性以及强大的并发处理能力使其成为构建高性能网络服务的首选语言之一。

G970620的博客 1678

可视化学习Go并发编程

在杭州站 Gopher Meetup 上,来自网易的黄庆兵分享一个相对轻松的话题——《可视化学习Go并发编程》。以下是他的演讲实录。 Go 天生是为并发而生的东西。简单来说,我觉得并发可以看作一种程序的构造方式,如下图所示。 并行&并发 想了解并行和并发的区别,推荐看一下Rob Pike的分享(点击阅读原文即可跳转)。在这个视频里,他用形象的Gop

Go中国 2282

【Go并发编程】开篇词 想吃透Go并发编程,你得这样学!_并发编程开篇词

我主要会介绍使用 etcd 实现的一些分布式并发原语,比如 Leader 选举、分布式互斥锁、分布式读写锁、分布式队列等,在处理分布式场景的并发问题时,特别有用。:主要有 Mutex、RWMutex、Waitgroup、Cond、Pool、Context 等标准库中的并发原语,这些都是传统的并发原语,在其它语言中也很常见,是我们在并发编程中常用的类型。每一位刚入门 Go 的程序员,在深入学习 Go 语言的时候,尤其是面对 Go 并发编程的时候,都会遇到这些问题。我们要做的,就是找出逻辑关系,拎出知识线。

2401_84240129的博客 496
上一篇: wsl2配置cuda和编译tensorflow
下一篇: Go学习(九):并发编程2
Lay_XZ
博客等级 码龄5年 29粉丝 14原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值