GO协程(Goroutine)问题总结

news2025/6/12 8:38:39

在使用Go语言来编写代码时,遇到的一些问题总结一下
[参考文档]:https://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/goroutine.html

1. main()函数默认的Goroutine

场景再现:

今天在看到这个教程的时候,在自己的电脑上跑了一下示例的代码。
发现了描述与我的执行结果不同的地方,官方文档描述如下:

这一次的执行结果只打印了main goroutine done!,并没有打印Hello Goroutine!。

但是我执行后的情况是,如图:
 main()函数默认的Goroutine

可以看到,我最终的执行结果是都输出了,而不是只输出了

main goroutine done!
Why?

原因是——虽然 main() 函数中调用了 go hello(),主 goroutine 在打印完 main goroutine done! 后就会退出,但:

在主 goroutine 退出前,如果新启动的 goroutine 有足够的时间运行完,Hello Goroutine! 就会输出。
上面这段代码启动了一个新 goroutine,但程序的执行是并发的,不是同步/阻塞的。

执行流程是:

go hello() 启动了一个新 goroutine;

fmt.Println("main goroutine done!") 被执行;

如果此时 main() 返回前,新 goroutine 还没来得及执行完,那它也会被强行终止;

但如果它已经执行完了,就能看到打印的内容。

这两句都成功输出,是因为你的电脑配置比较好,执行速度非常快,新启动的 goroutine 来得及在 main() 退出前完成打印。

正确做法:用 sync.WaitGroup 或 time.Sleep

2 . Go 协程(Goroutine)的两个关键点

协程不能保证执行的顺序,但是如果加了time.sleep的话,可以保障协程执行完毕

✅ Go 协程(Goroutine)的两个关键点:

①. 协程是并发的,不能保证执行顺序

go hello() 启动后,什么时候运行是由 Go 调度器决定的。
主协程和子协程是“谁抢到 CPU 谁先跑”,谁先打印是不确定的。

所以:

go hello()
fmt.Println("main done")

有可能先打印 main done,也可能先打印 Hello,取决于当时调度情况。

② 加 time.Sleep() 可以“间接保障”子协程执行完

加 time.Sleep() 相当于强行让主协程等一下,给子协程留时间执行完。

所以子协程通常会有时间执行完,看起来“像是被保障了执行”。
❗但注意:time.Sleep() ≠ 可靠同步
虽然 time.Sleep() 很简单,但它存在几个问题:

问题点说明
❌ 不精准你不知道子协程到底需要多少时间,sleep 多了浪费,少了又执行不完
❌ 不可扩展如果你有多个协程,就很难 sleep 到合适的时间
✅ 适合临时调试用于演示或实验是可以的

✅ 正确做法:用 sync.WaitGroup


import (
    "fmt"
    "sync"
)

func hello(wg *sync.WaitGroup) {
    fmt.Println("Hello Goroutine!")
    wg.Done() // 协程结束,通知 WaitGroup
}

func main() {
    var wg sync.WaitGroup
    wg.Add(1)        // 告诉 WaitGroup 等待 1 个协程
    go hello(&wg)    // 启动协程
    wg.Wait()        // 等待所有协程结束
    fmt.Println("main goroutine done!")
}

这样就能准确地等待协程执行完再退出,不用靠 sleep。

总结一句话:
time.Sleep() 是简单粗暴的等待方式,可以在小程序中“凑合用”,但真正写程序,用 sync.WaitGroup 等同步机制更稳、更准、更专业。

3.defer

3.1 defer使用对比
func hello(i int) {
    defer wg.Done() // goroutine结束就登记-1
    fmt.Println("Hello Goroutine!", i)
}func hello(i int){
    fmt.Println("Hello Goroutine!",i)
    wg.Done()
}
//在 正常情况下的效果是一样的:都会确保在 goroutine 执行完成后调用 wg.Done(),
//从而通知 WaitGroup,减少一个等待计数。

✅ defer wg.Done() 的优势:
defer 会在 函数返回前自动执行,即使函数中间发生了 panic(未被恢复),defer 也会运行(前提是没有让程序直接崩溃)。

这意味着:

func hello(i int) {
    defer wg.Done()
    // 如果这里出现错误,也能保证 Done 会执行
    fmt.Println("Hello Goroutine!", i)
}

更安全、稳健,防止遗漏。

❗ 手动调用 wg.Done() 有风险:

func hello(i int) {
    fmt.Println("Hello Goroutine!", i)
    wg.Done()
}

如果你写了更复杂的逻辑,中途 return 或 panic 了,wg.Done() 可能根本执行不到,就会导致 wg.Wait() 永远卡住。
✅ 总结:

写法是否推荐原因
defer wg.Done()✅ 推荐更安全,即使中途出错也能保证 Done 被调用
手动调用 wg.Done()⚠️ 慎用必须确保函数末尾一定能执行到,否则容易漏调用

所以你的判断是对的:“两种写法是一样的”,功能上是对的;但为了避免未来的问题,推荐使用 defer 写法,更健壮、易维护。

3.2defer的作用、在Goroutine中的使用案例以及执行顺序
3.2.1 defer的作用

✅ defer 的作用
defer 用于延迟一个函数的执行,直到外围函数(即当前函数)返回之前才调用。

换句话说:

无论当前函数中发生了什么(正常结束或提前 return),defer 注册的语句都会在函数结束前自动执行。

📌 举个例子说明:

func demo() {
    fmt.Println("start")
    defer fmt.Println("this is defer")
    fmt.Println("end")
}

输出结果是:

start
end
this is defer

defer fmt.Println(“this is defer”) 被延迟执行到 demo() 函数退出前的最后一刻。

✅ defer 的常见用途

用途示例说明
释放资源defer file.Close()防止文件忘记关闭
解锁defer mu.Unlock()防止死锁
记录日志/退出操作defer log.Println("退出")确保函数末尾执行
Done 通知defer wg.Done()保证 goroutine 退出时减少计数
3.2.2 在Goroutine中的使用案例
package main

import (
	"fmt"
	"sync"
)

var wg sync.WaitGroup

func hello(i int) {
	defer wg.Done() // 必须在协程结束时调用 wg.Done() 通知 WaitGroup 协程结束
	fmt.Println("Hello Goroutine!", i)
}
func main() {
	for i := 0; i < 10; i++ {
		wg.Add(1)   // 告诉 WaitGroup 等待 1 个协程
		go hello(i) // 启动另外一个goroutine去执行hello函数
	}
	wg.Wait() // 等待所有的 goroutine 结束
	fmt.Println("main goroutine done!")
}

3.2.3 多个defer的执行顺序

defer 在 Go 中的执行顺序就是栈结构的“先进后出(LIFO)”原则:

🧠 defer 的执行顺序:后注册,先执行
也就是:

func demo() {
    defer fmt.Println("1")
    defer fmt.Println("2")
    defer fmt.Println("3")
}

🧾 输出结果:

3
2
1

📦 原理解释:
当你写多个 defer 时,Go 会把它们压入一个栈中,函数结束时就从栈顶往下依次执行这些 defer。

行号执行内容进入 defer 栈
1defer fmt.Println("1")栈:[1]
2defer fmt.Println("2")栈:[1, 2]
3defer fmt.Println("3")栈:[1, 2, 3]
return前执行 defer从栈顶开始弹出输出顺序:3 → 2 → 1

✅ 常见用途
多个 defer 通常用于按顺序清理资源,比如:

func handleFile() {
    f1, _ := os.Open("file1.txt")
    defer f1.Close() // 最后关闭

    f2, _ := os.Open("file2.txt")
    defer f2.Close() // 比 f1 先关闭

    fmt.Println("doing something...")
}

结果是 file2 会先关闭,然后 file1 再关闭 —— 和你打开文件的顺序是相反的,这正是我们想要的行为(像“关门要倒着来”一样)。

✅ 总结:

特性说明
顺序多个 defer 是“后注册,先执行”(LIFO)
应用场景清理资源(文件、锁、连接等)、错误处理等
安全性即使函数 returnpanic,也会执行

4.主协程和其他协程的关系,主协退出了,其他的协程还执行吗?

代码使用了官网提供的:

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
		}
	}
}

执行结果:

main goroutine: i = 1
new goroutine: i = 1
main goroutine: i = 2
new goroutine: i = 2
new goroutine: i = 3

Process finished with the exit code 0

证明了主协程结束,其他线程不会再执行

5. java/c/c++线程与go协程的对比(与OS线程)

特性Java / C 的线程(OS Thread)Go 的 goroutine
线程类型操作系统线程(内核线程)用户级线程(协程)
线程模型1:1 模型M:N 模型
调度者操作系统Go 自带的调度器(runtime)
映射关系每个语言线程对应一个 OS 线程多个 goroutine 映射到多个 OS 线程
栈内存初始大小通常 1MB~2MB(固定)起始约 2KB(可动态伸缩)
创建成本高(需要系统调用)极低(用户态,几乎无开销)
调度成本高(内核态线程切换)低(用户态线程切换)
并发数量限制一般几千个十万甚至百万级
适合场景计算密集、高性能场景高并发、大量 I/O 场景
常用语言APIstd::thread, Threadgo myFunc()
内存使用效率相对较低非常高

🔍 示例类比:

类比Java / C 的线程Go 的 goroutine
比喻重型卡车:开销大但能干活自行车大军:轻量且灵活
调度员操作系统Go 自己的调度器
数量几千个已很吃力十万个都轻轻松松

✅ 图示说明

Java / C         =>        1:1 线程模型
┌──────────┐         ┌──────────┐
│ Thread A │───────▶│  OS 线程 A │
│ Thread B │───────▶│  OS 线程 B │
└──────────┘         └──────────┘

Go              =>        M:N 线程模型
┌──────────────┐
│ goroutine 1  │
│ goroutine 2  │
│ goroutine 3  │──┐
│ goroutine 4  │  │
│ goroutine 5  │  ├──▶ 被 Go runtime 调度
│ goroutine N  │──┘     分配到 OS 线程 A/B/C…
└──────────────┘

✅ 总结一句话:
Java 和 C 的线程就是系统线程(1:1),重量级。
Go 的 goroutine 是用户级线程,轻量可扩展(M:N),适合高并发。

6.并发与并行

6.1并发 vs 并行 示意图
+-----------------------+         +-------------------------+
|       单核 CPU        |         |        多核 CPU          |
| (1 个核心处理器核)     |         |   (多个核心同时工作)      |
+-----------------------+         +-------------------------+
|                       |         |                         |
|  ┌───────────────┐    |         |  ┌───────────────┐      |
|  │   核心 CPU 核  │    |         |  │  核心 CPU 核 1 │      |
|  │               │    |         |  ├───────────────┤      |
|  │ 任务1          │    |         |  │ 任务1 (并行执行)│      |
|  │任务2 (并发切换) │ ←──┐      |  │任务2 (并行执行)│      |
|  └───────────────┘    |  |      |  └───────────────┘      |
|  任务1和任务2“交替”执行 |  |      |  多个任务“真正同时”执行   |
|                       |  |      |                         |
+-----------------------+  |      +-------------------------+
                           |
         并发(Concurrency) |      并行(Parallelism)
         任务切换看似同时进行 |      物理上真正同时执行
6.2Go 的 G-P-M 调度模型简化示意
+----------------------------+
|           G (Goroutine)    |  // 轻量级任务
+----------------------------+
              │
              ▼
+----------------------------+
|         P (Processor)       |  // 逻辑处理器(虚拟CPU核)
|  数量 = GOMAXPROCS 的值     |
+----------------------------+
              │
              ▼
+----------------------------+
|       M (Machine/线程)      |  // 真实的 OS 线程
+----------------------------+
              │
              ▼
      真正执行代码的 CPU 核心

你创建的很多 G (goroutine),由有限个 P(由 GOMAXPROCS 控制) 调度到 M(操作系统线程) 上执行。

当 GOMAXPROCS=1,只有一个 P,也就是说同一时刻只有一个 M 在执行 goroutine,表现为“并发”,但不是“并行”。

当 GOMAXPROCS>1,多个 P 和 M 允许 goroutine 并行运行在多个 CPU 核心上。

6.3举个例子

🍳 并发(Concurrency):
就像一个厨师(单核 CPU),轮流做多个菜(任务)。他可能:


先把锅烧热,

然后去切菜,

再回来翻炒,

接着煮汤……

虽然这些菜好像都在“同时准备”,但其实是一个人来回切换工作状态完成的。

特点:


看起来同时,其实是轮流进行的

本质上是 任务调度,而不是并行执行

👨‍🍳👨‍🍳 并行(Parallelism):
多个厨师(多核 CPU)在多个灶台上,真正同时做多个菜(任务):

一人炒菜,

一人煮汤,

一人蒸饭……

特点:

真正同时执行

每个任务由不同核心独立运行

💡 套用到 Go 的场景里:

go func() 就像是安排一个菜让厨师做

runtime.GOMAXPROCS(1) 就是告诉厨房:“我只配一个厨师”(不管你有几个灶台)

Go 的调度器就像厨房总管,会安排炒什么菜、先做哪个、后做哪个

7. 缓冲信道和无缓冲信道

🌟 一张表格对比
特性无缓冲通道 (make(chan T))带缓冲通道 (make(chan T, N))
是否有缓冲区❌ 没有,容量为 0✅ 有,容量为 N(N ≥ 1)
是否阻塞发送方✅ 会,直到接收方准备好❌ 不会,只要缓冲区没满
是否阻塞接收方✅ 会,直到发送方发送❌ 不会,只要缓冲区有数据
典型应用同步通信,控制执行时机异步通信,解耦发送与接收
性能慢,但同步、控制精准快,但可能增加复杂性
死锁风险高(若不成对出现)低(发送和接收可以脱钩)
是否 FIFO✅ 是✅ 是
✅ 示例对比:无缓冲通道

package main

import "fmt"

func main() {
	ch := make(chan int) // 无缓冲通道

	go func() {
		val := <-ch
		fmt.Println("接收成功:", val)
	}()

	ch <- 10              // 阻塞直到接收方准备好
	fmt.Println("发送成功")
}

执行顺序:

接收成功: 10
发送成功

👉 注意:如果你把 发送(ch <- 10) 放在 go 启动接收协程 之前,就会死锁!

✅ 示例对比:带缓冲通道
package main

import "fmt"

func main() {
	ch := make(chan int, 1) // 带缓冲通道,容量为1

	ch <- 10                // ✅ 不阻塞
	fmt.Println("发送成功")

	go func() {
		val := <-ch
		fmt.Println("接收成功:", val)
	}()
}

执行顺序:

发送成功
接收成功: 10

👉 注意:即使接收方还没准备好,发送也不会阻塞(只要缓冲区有空间)。

🧠 总结一句话:
无缓冲通道:必须“手递手”,发送和接收必须同步发生。

带缓冲通道:允许“放下再走”,先发送存储,之后再接收。

🤔我的理解【ASK ChatGPT】
我的理解

无论是先发送还是先接收,都不应该主程序阻塞后续的程序,否则接收不到,或者发送不了。
无论先发送还是接收,都应该在下一步前建立一个协程来进行类似监听的协程来阻塞协程这样才能保证发送接收同步进行吗?
无论是发送(val<-10),还是接收(<-val)都会阻塞当前的协程,我说的对吗

GPT回答

✅ 你的原话分析:

❓“无论是先发送还是先接收,都不应该主程序阻塞后续的程序,否则接收不到,或者发送不了。”
✅ 完全正确!

如果你在 main() 主线程中直接执行 val := <-ch 或 ch <- 10,主 goroutine 会阻塞,导致之后的 goroutine 根本不会被调度运行。

所以要让 发送/接收那一方跑在 goroutine 中,才能让另一方执行并完成通信。

❓“无论先发送还是接收,都应该在下一步前建立一个协程来进行类似监听的协程来阻塞协程这样才能保证发送接收同步进行吗?”
✅ 非常正确,表达也很清晰。

Go 的无缓冲通道通信是 两个 goroutine 的同步配合行为。

不论谁先运行,都必须有 另一个 goroutine 在等待,通信才能发生。

✅ 举个常用结构例子:

go func() {
    ch <- 10  // goroutine A 发送
}()

go func() {
    val := <-ch  // goroutine B 接收
}()

两个 goroutine 各自阻塞在 send/recv 点,一旦两者都就绪,通信立刻完成。

❓“无论是发送(val <- 10),还是接收(<-val)都会阻塞当前的协程,我说的对吗?”
✅ 完全正确!

操作是否阻塞说明
ch <- 10✅ 会阻塞如果没有协程在接收,就阻塞
<-ch✅ 会阻塞如果没有协程在发送,就阻塞

✅ 只要通道是无缓冲的,这两个操作都必须等对方准备好才能继续。

✅ 最核心的一句话总结:
无缓冲通道中的发送和接收,必须发生在两个独立的 goroutine 中,才能顺利配对并完成同步。

8. channel关闭(close)

8.1 channel 关闭(close)≠ 销毁

Go 里没有显式的“销毁”channel操作,只有 关闭(close) 操作。

close(ch) 表示:

禁止再向通道写入数据(写入会 panic)

可以继续读取通道中剩余的缓冲数据

读取到空了之后,再读取会得到零值(对应类型的默认值)并返回 ok=false
8.2 关闭 channel 后为什么“只能读取一次”?
通道关闭后,只能读取到剩余的数据,读取完就没有了。

你可以多次从关闭的 channel 读取数据,直到读完所有剩余数据。

但是如果你用 for range ch 循环,循环会在读完所有数据后自动结束。

重点:
for range ch 循环 只能执行一次,因为通道中的数据只会被消费一次,且通道关闭后不再接收新数据。

8.3 不关闭 channel,能读多次吗?
如果 不关闭 channel,就不能用 for range ch 这种方式循环读取,因为它会阻塞等待新的数据,直到 channel 关闭。

但你可以用手动读取(val, ok := <-ch)不断读数据,只要有人往里面写,就能持续接收。

多个接收者从同一个 channel 读取是“抢占式”的,数据会被分配给其中一个,不能保证所有接收者都能读到相同数据。
8.4 总结
状态是否能写入是否能读取多次备注
channel 已关闭不允许(写会 panic)可以读取剩余数据,读完返回零值+ok=false只能消费剩余数据,不能写入
channel 未关闭允许可以多次读取(取决于写入情况)读取会阻塞等待数据
  1. 举个例子说明
package main

import "fmt"

func main() {
	ch := make(chan int, 3)
	ch <- 1
	ch <- 2
	ch <- 3

	close(ch) // 关闭通道

	for i := 0; i < 5; i++ {
		val, ok := <-ch
		fmt.Println(val, ok)
	}
}

输出:

1 true
2 true
3 true
0 false  // 通道关闭且无剩余数据,返回类型零值,ok=false
0 false

如果你想多次“重新读取”同一批数据,就需要把数据保存到 slice 或其他容器,而不是依赖 channel 本身,因为 channel 数据是一次性消费的。

9.匿名函数(内联函数)

官方文档的Goroutine池中提供了

package main

import (
	"fmt"
	"math/rand"
)

type Job struct {
	// id
	Id int
	// 需要计算的随机数
	RandNum int
}

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

func main() {
	// 需要2个管道
	// 1.job管道
	jobChan := make(chan *Job, 128)
	// 2.结果管道
	resultChan := make(chan *Result, 128)
	// 3.创建工作池
	createPool(64, jobChan, resultChan)
	// 4.开个打印的协程
	go func(resultChan chan *Result) {
		// 遍历结果管道打印
		for result := range resultChan {
			fmt.Printf("job id:%v randnum:%v result:%d\n", result.job.Id,
				result.job.RandNum, result.sum)
		}
	}(resultChan) //①匿名函数
	var id int
	// 循环创建job,输入到管道
	for {
		id++
		// 生成随机数
		r_num := rand.Int()
		job := &Job{
			Id:      id,
			RandNum: r_num,
		}
		jobChan <- job
	}
}

// 创建工作池
// 参数1:开几个协程
func createPool(num int, jobChan chan *Job, resultChan chan *Result) {
	// 根据开协程个数,去跑运行
	for i := 0; i < num; i++ {
		go func(jobChan chan *Job, resultChan chan *Result) {
			// 执行运算
			// 遍历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
				}
				// 想要的结果是Result
				r := &Result{
					job: job,
					sum: sum,
				}
				//运算结果扔到管道
				resultChan <- r
			}
		}(jobChan, resultChan)//②匿名函数
	}
}

对②匿名函数解释

  • (jobChan, resultChan)createPool(num int, jobChan chan *Job, resultChan chan *Result)处对应的参数值,
  • 然后(jobChan, resultChan)又被当作参数传入了go func(jobChan chan *Job, resultChan chan *Result)

用匿名函数的好处是 不需要额外写一个函数名,逻辑上简洁一些。

10.select处理多种类型事件例子

常见用法举例
假设你有几个不同类型的事件通过不同通道传递,你可以写类似的代码:

package main

import (
	"fmt"
	"time"
)

func eventA(ch chan string) {
	time.Sleep(time.Second * 1)
	ch <- "Event A occurred"
}

func eventB(ch chan int) {
	time.Sleep(time.Second * 2)
	ch <- 42
}

func eventC(ch chan bool) {
	time.Sleep(time.Second * 3)
	ch <- true
}

func main() {
	chA := make(chan string)
	chB := make(chan int)
	chC := make(chan bool)

	go eventA(chA)
	go eventB(chB)
	go eventC(chC)

	for i := 0; i < 3; i++ {
		select {
		case msg := <-chA:
			fmt.Println("Received from chA:", msg)
		case val := <-chB:
			fmt.Println("Received from chB:", val)
		case flag := <-chC:
			fmt.Println("Received from chC:", flag)
		}
	}
}

解释

  • 你启动3个 goroutine,它们各自延迟不同时间后往对应通道写入不同类型的事件数据。

  • 主协程中用一个 select 监听这3个通道。

  • 每当任意一个通道有数据,就进入对应 case 执行,打印接收到的事件。

  • 循环3次保证3个事件都能被处理。

重点总结

  • select 可以监听任意多个通道。

  • 每个 case 可以处理不同类型的数据。

  • 只要某个通道准备好数据,select 就会立即触发对应的 case。

  • select 本身不是“处理多种类型数据”的结构,但结合不同通道的类型可以做到这一点。

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

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

相关文章

C++ 设计模式 《小明的奶茶加料风波》

&#x1f468;‍&#x1f393; 模式名称&#xff1a;装饰器模式&#xff08;Decorator Pattern&#xff09; &#x1f466; 小明最近上线了校园奶茶配送功能&#xff0c;业务火爆&#xff0c;大家都在加料&#xff1a; 有的同学要加波霸 &#x1f7e4;&#xff0c;有的要加椰果…

【MATLAB代码】基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),附源代码|订阅专栏后可直接查看

文章所述的代码实现了基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),针对传感器观测数据中存在的脉冲型异常噪声问题,通过非线性加权机制提升滤波器的抗干扰能力。代码通过对比传统KF与MCC-KF在含异常值场景下的表现,验证了后者在状态估计鲁棒性方面的显著优…

代码规范和架构【立芯理论一】(2025.06.08)

1、代码规范的目标 代码简洁精炼、美观&#xff0c;可持续性好高效率高复用&#xff0c;可移植性好高内聚&#xff0c;低耦合没有冗余规范性&#xff0c;代码有规可循&#xff0c;可以看出自己当时的思考过程特殊排版&#xff0c;特殊语法&#xff0c;特殊指令&#xff0c;必须…

解读《网络安全法》最新修订,把握网络安全新趋势

《网络安全法》自2017年施行以来&#xff0c;在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂&#xff0c;网络攻击、数据泄露等事件频发&#xff0c;现行法律已难以完全适应新的风险挑战。 2025年3月28日&#xff0c;国家网信办会同相关部门起草了《网络安全…

计算机基础知识解析:从应用到架构的全面拆解

目录 前言 1、 计算机的应用领域&#xff1a;无处不在的数字助手 2、 计算机的进化史&#xff1a;从算盘到量子计算 3、计算机的分类&#xff1a;不止 “台式机和笔记本” 4、计算机的组件&#xff1a;硬件与软件的协同 4.1 硬件&#xff1a;五大核心部件 4.2 软件&#…

C# 表达式和运算符(求值顺序)

求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如&#xff0c;已知表达式3*52&#xff0c;依照子表达式的求值顺序&#xff0c;有两种可能的结果&#xff0c;如图9-3所示。 如果乘法先执行&#xff0c;结果是17。如果5…

打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用

一、方案背景​ 在现代生产与生活场景中&#xff0c;如工厂高危作业区、医院手术室、公共场景等&#xff0c;人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式&#xff0c;存在效率低、覆盖面不足、判断主观性强等问题&#xff0c;难以满足对人员打手机行为精…

[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.

ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #&#xff1a…

逻辑回归暴力训练预测金融欺诈

简述 「使用逻辑回归暴力预测金融欺诈&#xff0c;并不断增加特征维度持续测试」的做法&#xff0c;体现了一种逐步建模与迭代验证的实验思路&#xff0c;在金融欺诈检测中非常有价值&#xff0c;本文作为一篇回顾性记录了早年间公司给某行做反欺诈预测用到的技术和思路。百度…

FFmpeg:Windows系统小白安装及其使用

一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】&#xff0c;注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录&#xff08;即exe所在文件夹&#xff09;加入系统变量…

[ACTF2020 新生赛]Include 1(php://filter伪协议)

题目 做法 启动靶机&#xff0c;点进去 点进去 查看URL&#xff0c;有 ?fileflag.php说明存在文件包含&#xff0c;原理是php://filter 协议 当它与包含函数结合时&#xff0c;php://filter流会被当作php文件执行。 用php://filter加编码&#xff0c;能让PHP把文件内容…

Golang——9、反射和文件操作

反射和文件操作 1、反射1.1、reflect.TypeOf()获取任意值的类型对象1.2、reflect.ValueOf()1.3、结构体反射 2、文件操作2.1、os.Open()打开文件2.2、方式一&#xff1a;使用Read()读取文件2.3、方式二&#xff1a;bufio读取文件2.4、方式三&#xff1a;os.ReadFile读取2.5、写…

从 GreenPlum 到镜舟数据库:杭银消费金融湖仓一体转型实践

作者&#xff1a;吴岐诗&#xff0c;杭银消费金融大数据应用开发工程师 本文整理自杭银消费金融大数据应用开发工程师在StarRocks Summit Asia 2024的分享 引言&#xff1a;融合数据湖与数仓的创新之路 在数字金融时代&#xff0c;数据已成为金融机构的核心竞争力。杭银消费金…

R 语言科研绘图第 55 期 --- 网络图-聚类

在发表科研论文的过程中&#xff0c;科研绘图是必不可少的&#xff0c;一张好看的图形会是文章很大的加分项。 为了便于使用&#xff0c;本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中&#xff0c;获取方式&#xff1a; R 语言科研绘图模板 --- sciRplothttps://mp.…

LabVIEW双光子成像系统技术

双光子成像技术的核心特性 双光子成像通过双低能量光子协同激发机制&#xff0c;展现出显著的技术优势&#xff1a; 深层组织穿透能力&#xff1a;适用于活体组织深度成像 高分辨率观测性能&#xff1a;满足微观结构的精细研究需求 低光毒性特点&#xff1a;减少对样本的损伤…

【 java 虚拟机知识 第一篇 】

目录 1.内存模型 1.1.JVM内存模型的介绍 1.2.堆和栈的区别 1.3.栈的存储细节 1.4.堆的部分 1.5.程序计数器的作用 1.6.方法区的内容 1.7.字符串池 1.8.引用类型 1.9.内存泄漏与内存溢出 1.10.会出现内存溢出的结构 1.内存模型 1.1.JVM内存模型的介绍 内存模型主要分…

(一)单例模式

一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…

day36-多路IO复用

一、基本概念 &#xff08;服务器多客户端模型&#xff09; 定义&#xff1a;单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用&#xff1a;应用程序通常需要处理来自多条事件流中的事件&#xff0c;比如我现在用的电脑&#xff0c;需要同时处理键盘鼠标…

群晖NAS如何在虚拟机创建飞牛NAS

套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享

android13 app的触摸问题定位分析流程

一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…