首页
关于
Search
1
Golang闭包
10 阅读
2
ROS2(二):第一个自定义 Python 节点从编写到运行
9 阅读
3
Golang并发
7 阅读
4
Golang匿名函数
3 阅读
5
Golang Time包入门
2 阅读
默认分类
编程
ROS
登录
Search
tqtqtq
累计撰写
11
篇文章
累计收到
1
条评论
首页
栏目
默认分类
编程
ROS
页面
关于
搜索到
8
篇与
的结果
2026-02-06
Golang Time包入门
Golang Time包入门一、基础概念Time 包主要用于处理时间相关的操作,比如获取当前时间、时间计算、格式化等。1.获取当前时间package main import ( "fmt" "time" ) func main() { // 获取当前时间 now := time.Now() fmt.Println("现在是:", now) } //输出: //现在是: 2026-02-05 22:19:18.4958836 +0800 CST m=+0.0000000012. 时间的组成部分now := time.Now() fmt.Println("年:", now.Year()) fmt.Println("月:", now.Month()) // 英文月份 fmt.Println("月:", int(now.Month())) // 数字月份 fmt.Println("日:", now.Day()) fmt.Println("时:", now.Hour()) fmt.Println("分:", now.Minute()) fmt.Println("秒:", now.Second()) fmt.Println("星期:", now.Weekday()) //输出: //年: 2026 //月: February //月: 2 //日: 5 //时: 22 //分: 20 //秒: 25 //星期: Thursday二、时间格式化Golang 的时间格式化比较特殊,使用 2006-01-02 15:04:05 这个固定时间作为模板。now := time.Now() // 常用格式 fmt.Println(now.Format("2006-01-02")) // 2024-02-05 fmt.Println(now.Format("2006-01-02 15:04:05")) // 2024-02-05 14:30:25 fmt.Println(now.Format("15:04:05")) // 14:30:25 fmt.Println(now.Format("2006/01/02")) // 2024/02/05 fmt.Println(now.Format("01-02-2006")) // 02-05-2024 //输出: //2026-02-05 //2026-02-05 22:22:57 //22:22:57 //2026/02/05 //02-05-2026三、时间间隔 DurationGo 语言不使用整数(int)直接代表秒或毫秒,而是用 time.Duration 类型。Duration 表示两个时间点之间的间隔,常用于延时、超时等场景。// 常用的时间单位 fmt.Println(time.Second) // 1秒 fmt.Println(time.Minute) // 1分钟 fmt.Println(time.Hour) // 1小时 // 自定义时间间隔 d := 3 * time.Second // 3秒 d2 := 500 * time.Millisecond // 500毫秒 d3 := 2 * time.Hour // 2小时 //如果有一个变量 n := 5,想休眠 n 秒,不能写 n * time.Second,必须强转:time.Duration(n) * time.Second四、常用操作1. 时间睡眠(暂停程序)fmt.Println("开始") time.Sleep(2 * time.Second) // 暂停2秒 fmt.Println("2秒后")2. 时间加减now := time.Now() // 加时间 future := now.Add(24 * time.Hour) // 加1天 fmt.Println("明天:", future) past := now.Add(-1 * time.Hour) // 减1小时 fmt.Println("1小时前:", past) // 加日期 nextMonth := now.AddDate(0, 1, 0) // 加1个月 fmt.Println("下个月:", nextMonth) nextYear := now.AddDate(1, 0, 0) // 加1年 yesterday := now.AddDate(0, 0, -1) // 减1天3. 时间比较t1 := time.Now() time.Sleep(1 * time.Second) t2 := time.Now() // 判断时间先后 if t2.After(t1) { fmt.Println("t2 在 t1 之后") } if t1.Before(t2) { fmt.Println("t1 在 t2 之前") } // 计算时间差 diff := t2.Sub(t1) fmt.Println("相差:", diff)五、多线程中常用的 Time 操作1. 定时器 Timer(执行一次)// 3秒后执行 timer := time.NewTimer(3 * time.Second) go func() { <-timer.C // 等待定时器触发 fmt.Println("定时器触发了!") }() time.Sleep(5 * time.Second) // 主程序等待2. 周期执行 Ticker// 每隔1秒执行一次 ticker := time.NewTicker(1 * time.Second) go func() { for t := range ticker.C { fmt.Println("滴答:", t.Format("15:04:05")) } }() time.Sleep(5 * time.Second) ticker.Stop() // 停止定时器3. 超时控制// 方法1:使用 time.After select { case <-time.After(3 * time.Second): fmt.Println("超时了!") case result := <-someChannel: fmt.Println("收到结果:", result) } // 方法2:使用 context(更推荐) ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() select { case <-ctx.Done(): fmt.Println("超时:", ctx.Err()) }
2026年02月06日
2 阅读
0 评论
0 点赞
2026-02-04
Golang匿名函数
Golang匿名函数什么是匿名函数匿名函数就是没有名字的函数,也称为函数字面量(function literal)。它可以在代码中直接定义和使用,无需事先声明函数名。基本语法func(参数列表) 返回值类型 { 函数体 }使用方法1.定义后立即调用package main import "fmt" func main() { // 定义并立即执行 func() { fmt.Println("这是一个匿名函数") }() // 带参数和返回值 result := func(a, b int) int { return a + b }(3, 5) fmt.Println("结果:", result) // 输出: 结果: 8 }2.赋值给变量package main import "fmt" func main() { // 将匿名函数赋值给变量 add := func(a, b int) int { return a + b } // 通过变量调用 fmt.Println(add(10, 20)) // 输出: 30 // 变量可以重新赋值 add = func(a, b int) int { return a * b } fmt.Println(add(10, 20)) // 输出: 200 }3.作为函数参数package main import "fmt" // 接收函数作为参数 func operate(a, b int, op func(int, int) int) int { return op(a, b) } func main() { // 传入匿名函数 result := operate(10, 5, func(x, y int) int { return x - y }) fmt.Println("结果:", result) // 输出: 结果: 5 }4.作为返回值package main import "fmt" // 返回一个函数 func getOperator(op string) func(int, int) int { switch op { case "+": return func(a, b int) int { return a + b } case "*": return func(a, b int) int { return a * b } default: return func(a, b int) int { return 0 } } } func main() { addFunc := getOperator("+") fmt.Println(addFunc(3, 4)) // 输出: 7 mulFunc := getOperator("*") fmt.Println(mulFunc(3, 4)) // 输出: 12 }
2026年02月04日
3 阅读
0 评论
0 点赞
2026-02-04
Golang闭包
闭包什么是闭包?一句话理解:闭包就是一个“自带背包”的函数在普通的函数调用中,函数执行完,里面的变量就会被销毁(内存被释放)。闭包(Closure)是函数与其相关的引用环境组合而成的实体。简单说,闭包是一个函数,它能"记住"并访问创建它的上下文中的变量,即使这个函数在其原始作用域之外执行。闭包 = 函数 + 引用环境更准确地说:闭包是一个函数这个函数可以访问其外部函数作用域中的变量即使外部函数已经执行完毕,闭包仍然可以访问这些变量在 Go 中,当一个匿名函数引用了其外部作用域的变量时,就形成了闭包。代码拆解为了看懂闭包,我们需要先理解 Go 语言中函数的两个特权:函数可以定义在另一个函数里面(匿名函数)。函数可以作为返回值返回。第一步:看一个简单的计数器需求如果我们要写一个计数器,每调用一次就加 1。普通写法(使用全局变量):var i int = 0 // 全局变量 func count() int { i++ return i } // 缺点:i 是全局的,谁都能改它,不安全。闭包写法(保护变量):闭包的“标准模板”:// accumulation 是一个函数,它返回值的类型是一个 func() int func accumulator() func() int { // 1. 定义一个“外部”变量 sum := 0 // 2. 定义一个内部匿名函数,并返回它 return func() int { // 3. 这个内部函数引用了外部的 sum sum++ return sum } } func main() { // 创建一个闭包实例,赋值给变量 f f := accumulator() fmt.Println(f()) // 输出: 1 fmt.Println(f()) // 输出: 2 fmt.Println(f()) // 输出: 3 }func accumulator() func() int: 外层函数。它不直接返回数字,而是返回一个动作(另一个函数)。sum := 0: 这是定义在外层环境的变量。按理说 accumulator 执行完,sum 应该销毁。return func() int {...}: 我们返回了内部函数。关键点来了: 这个内部函数在使用 sum。捕获(Capture): Go 编译器发现内部函数要用 sum,也是内部函数被返回到了外面。编译器会判定:“sum不能销毁,因为返回出去的那个东西还要用它”。于是,sum 就被装进了内部函数的“背包”里,一起带出了 accumulator 函数。闭包的原理作用域的概念在理解闭包之前,我们需要了解作用域:package main import "fmt" var global = "我是全局变量" // 全局作用域 func outer() { outerVar := "我是外部函数变量" // outer 函数作用域 func inner() { innerVar := "我是内部函数变量" // inner 函数作用域 // inner 可以访问: fmt.Println(innerVar) // 自己的变量 fmt.Println(outerVar) // 外部函数的变量 fmt.Println(global) // 全局变量 } inner() // fmt.Println(innerVar) // 错误! outer 访问不了 inner 的变量 } func main() { outer() }闭包如何"捕获"变量package main import "fmt" func createCounter() func() int { count := 0 // 这个变量会被"捕获" // 返回的这个匿名函数形成了闭包 return func() int { count++ // 访问并修改被捕获的变量 return count } } func main() { counter := createCounter() fmt.Println(counter()) // 1 fmt.Println(counter()) // 2 fmt.Println(counter()) // 3 // count 变量在 createCounter 执行完后仍然存在! }引用还是复制?重要概念:闭包捕获的是变量的引用,不是值的拷贝!package main import "fmt" func main() { x := 10 // 闭包捕获的是 x 的引用 increment := func() { x++ // 修改的是同一个 x } fmt.Println("x =", x) // 10 increment() fmt.Println("x =", x) // 11 increment() fmt.Println("x =", x) // 12 // x 被修改了! }基础用法1. 捕获并修改外部变量闭包最基本的特性就是可以访问并修改其外部作用域的变量,因为闭包持有的是变量的引用,而不是值的拷贝,所以修改会影响原变量。func main() { x := 10 // 闭包函数引用外部变量x increment := func() int { x++ return x } fmt.Println(increment()) // 11 fmt.Println(increment()) // 12 fmt.Println(x) // 12 }2. 函数工厂模式闭包常用于创建定制化的函数,每个闭包都维护自己的状态。可以用来创建具有不同配置的相似函数,避免重复代码。函数工厂(Function Factory):通过一个工厂函数,根据不同的配置参数,批量生产具有特定行为的功能函数。// 工厂函数模板 func makeXXX(config) func(...) ... { // 配置被"封装"在闭包中 return func(...) ... { // 使用配置执行操作 } }e.g.简单案例——乘数工厂package main import "fmt" // makeMultiplier 是工厂,factor是配置,返回的函数是产品 func makeMultiplier(factor int) func(int) int { // factor被闭包捕获,每个返回的函数"记住"自己的factor return func(x int) int { return x * factor } } func main() { // 生产不同配置的乘数器 double := makeMultiplier(2) triple := makeMultiplier(3) tenfold := makeMultiplier(10) // 每个产品独立运行,互不干扰 fmt.Println(double(5)) // 10 fmt.Println(triple(5)) // 15 fmt.Println(tenfold(5)) // 50 // 验证状态隔离:修改一个不影响其他 double2 := makeMultiplier(2) fmt.Println(double(100)) // 200 fmt.Println(double2(100)) // 200 (相同配置,独立实例) }每次调用makeMultiplier,都会创建一个新的factor变量副本,被返回的闭包专属持有。案例场景:配置化HTTP客户端package main import ( "fmt" "time" ) // HTTPClient 配置 type HTTPConfig struct { BaseURL string Timeout time.Duration RetryCount int Headers map[string]string } // 工厂函数:根据配置创建专用的请求函数 func makeHTTPRequester(config HTTPConfig) func(endpoint string) string { // 预加载配置(只解析一次) fullBase := config.BaseURL if !endsWithSlash(fullBase) { fullBase += "/" } return func(endpoint string) string { // 闭包内使用预处理的配置 url := fullBase + endpoint // 模拟请求逻辑 result := fmt.Sprintf("请求: %s (超时:%v, 重试:%d次)", url, config.Timeout, config.RetryCount) return result } } func endsWithSlash(s string) bool { return len(s) > 0 && s[len(s)-1] == '/' } func main() { // 生产环境客户端 prodClient := makeHTTPRequester(HTTPConfig{ BaseURL: "https://api.example.com", Timeout: 5 * time.Second, RetryCount: 3, }) // 测试环境客户端 testClient := makeHTTPRequester(HTTPConfig{ BaseURL: "http://localhost:8080", Timeout: 30 * time.Second, RetryCount: 0, }) // 使用客户端(配置已内嵌,调用简洁) fmt.Println(prodClient("users/1")) fmt.Println(testClient("users/1")) fmt.Println(prodClient("orders/list")) }输出:请求: https://api.example.com/users/1 (超时:5s, 重试:3次) 请求: http://localhost:8080/users/1 (超时:30s, 重试:0次) 请求: https://api.example.com/orders/list (超时:5s, 重试:3次)闭包陷阱1.循环变量陷阱(注意Go 1.2.2+有所不同)package main import ( "fmt" "time" ) func main() { // 错误示例 for i := 0; i < 5; i++ { go func() { fmt.Println(i) // 所有goroutine共享同一个i }() } time.Sleep(time.Second) } // 输出:可能全是 5 5 5 5 5解析:所有 goroutine 捕获的是同一个变量 i 的引用,而不是值。当 goroutine 执行时,循环可能已经结束,i 的值变成了 5。问题核心:在 Go 1.22 版本之前,for 循环中的变量在内存中只有一份,而不是每次循环都生成一个新的。解决:// 方法1: 传参 for i := 0; i < 5; i++ { go func(n int) { fmt.Println(n) }(i) // 将i的当前值传入 } // 方法2: 创建局部副本 for i := 0; i < 5; i++ { i := i // 创建新的局部变量 go func() { fmt.Println(i) }() } // 方法3: Go 1.22+ 版本自动修复 // 从 Go 1.22 开始,for 循环会为每次迭代创建新变量2.延迟函数中的闭包陷阱func main() { x := 10 // 写法1: 传参 defer func(val int) { fmt.Println("A:", val) }(x) // 写法2: 闭包捕获 defer func() { fmt.Println("B:", x) }() x = 20 } //输出: //B: 20 //A: 10解析:写法1 (传参): defer 在声明时就会对函数的参数进行求值(Evaluate)。当时 x 是 10,所以 10 被拷贝进去了。写法2 (闭包): defer 在声明时只记录了要执行函数,函数内部引用的 x 依然是对外部变量的引用。当函数在 main 结束前真正执行时,x 已经被修改为 20。常见的混淆点:求值时机。
2026年02月04日
10 阅读
0 评论
0 点赞
1
2