从零开始学 Go:协程并发与 Web 开发初探

从零开始学 Go:协程并发与 Web 开发初探

前言

最近开始系统学习 Go 语言,被它简洁的语法和强大的并发模型所吸引。本文是我学习过程中的一些笔记和代码整理,从最基础的语法到协程并发,再到用 Gin 搭建 Web 服务,希望能帮助同样在入门 Go 的同学快速上手。文章会由浅入深,配合可运行的代码示例,让你边看边练。

一、初识 Go:Hello World 与基础语法

1.1 第一个 Go 程序

Go 程序的入口是 main 包下的 main 函数,使用 fmt.Println 输出内容。

go 复制代码
package main

import "fmt"

func main() {
    fmt.Println("hello world")
}

运行方式:

bash 复制代码
go run main.go

1.2 变量声明

Go 是强类型语言,声明变量有两种常用方式:

go 复制代码
// 显式声明类型
var name string = "你好"

// 短变量声明,类型自动推断(只能在函数内使用)
age := 18

注意:age := 18 推断为 int 类型,之后不能再赋值为字符串 age = "22",编译器会报错。

1.3 条件与循环

Go 的 if 语句不需要括号包裹条件,循环只有 for 一种关键字,但可以实现传统 while 的效果。

go 复制代码
if age >= 18 {
    fmt.Println("成年人")
}

// 经典 for 循环
for i := 0; i < 10; i++ {
    fmt.Println(i)
}

1.4 数组、切片与映射

  • 数组 :长度固定,如 [3]int{1,2,3}
  • 切片 :动态数组,使用更灵活,支持 append 追加元素
  • 映射:键值对集合
go 复制代码
// 切片
slice := []int{1, 2, 3}
slice = append(slice, 4) // [1 2 3 4]

// map
m := map[string]int{"a": 1, "b": 2}
fmt.Println(m["a"]) // 输出: 1

1.5 函数与结构体

Go 没有类的概念,但可以通过结构体和方法实现面向对象风格。

go 复制代码
// 普通函数
func add(a int, b int) int {
    return a + b
}

// 结构体定义
type User struct {
    Name string
    Age  int
}

// 创建实例
u := User{Name: "张三", Age: 18}

1.6 指针

Go 支持指针,常用于函数中修改外部变量。

go 复制代码
func updateAge(age *int) {
    *age = 20
}

x := 18
updateAge(&x)
fmt.Println(x) // 20

二、并发编程:goroutine 与 channel

2.1 什么是 goroutine?

Go 语言并发模型的核心是 goroutine,它是由 Go 运行时管理的轻量级线程。你可以把它理解为"协程",但比传统线程更轻量、开销更小,可以轻松创建成千上万个。

用一个厨房比喻来理解:

厨师一次只做一道菜,如果肉菜需要炖 20 分钟,他不用干等,而是利用这段时间去切土豆、准备其他食材------这就相当于 并发,多个任务交替推进,看起来像同时进行。

2.2 开启一个 goroutine

使用 go 关键字即可在后台启动一个新的 goroutine。

go 复制代码
package main

import (
    "fmt"
    "time"
)

func sayHello() {
    fmt.Println("hello world")
}

func main() {
    go sayHello()           // 新开协程执行
    fmt.Println("main")
    time.Sleep(time.Second) // 阻塞主线程,等待协程完成
}

注意 :如果主函数退出,所有 goroutine 都会被强制结束,所以这里用 time.Sleep 简单等待(实际开发中应使用同步机制)。

2.3 通道 channel:协程间通信

goroutine 之间可以通过 channel 安全地传递数据。channel 是类型相关的管道,你可以向它发送数据,也可以从它接收数据。

go 复制代码
package main

import "fmt"

func main() {
    ch := make(chan int) // 创建传递 int 的通道

    go func() {
        ch <- 100 // 发送数据到通道
    }()

    num := <-ch // 从通道接收数据,此处会阻塞等待
    fmt.Println(num) // 100
}

发送和接收操作默认是阻塞的,这天然实现了 goroutine 之间的同步。

三、Web 开发:从 net/http 到 Gin 框架

3.1 标准库搭建 Web 服务器

Go 标准库 net/http 已经提供了基本的 HTTP 服务能力,无需第三方框架即可启动 Web 服务。

go 复制代码
package main

import (
    "fmt"
    "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "hello world")
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}

访问 http://localhost:8080 即可看到响应。

3.2 使用 Gin 框架

Gin 是 Go 生态中最流行的 Web 框架之一,类似于 Node.js 中的 Express,它以高性能和简洁的 API 著称,非常适合构建 RESTful 服务。

首先安装 Gin:

bash 复制代码
go get -u github.com/gin-gonic/gin

然后编写一个简单的 JSON 接口:

go 复制代码
package main

import "github.com/gin-gonic/gin"

func main() {
    r := gin.Default()

    r.GET("/hello", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "hello world",
        })
    })

    r.Run(":8080")
}
  • gin.Default() 创建默认路由器(自带 Logger 和 Recovery 中间件)
  • r.GET 注册路由和处理函数
  • c.JSON 返回 JSON 格式响应
  • gin.Hmap[string]interface{} 的快捷写法

运行后访问 http://localhost:8080/hello,即可获得 JSON 数据。

3.3 项目依赖管理

你看到的 go.mod 文件是 Go Modules 的配置文件,记录了项目的模块名和依赖版本。当你在项目中导入 Gin 等第三方包时,go.mod 会自动更新依赖树,保证构建的可重现性。

go 复制代码
module gin-demo

go 1.26.2

require (
    github.com/gin-gonic/gin v1.12.0
    // ... 其他间接依赖
)

四、总结与展望

通过以上内容,我们从零开始掌握了 Go 语言的:

  • 基础语法:变量、循环、切片、结构体、指针
  • 并发模型:goroutine 与 channel 的基本使用
  • Web 开发:用标准库和 Gin 框架搭建 HTTP 服务

Go 语言的设计哲学是"少即是多",语法简单但功能强大,尤其在并发和网络编程领域表现突出。后续可以进一步学习:

  • 错误处理与 defer
  • 接口 interface 的妙用
  • 更复杂的并发模式(select、sync 包)
  • 数据库集成(如 MongoDB 驱动)
  • 项目结构设计

如果你也对 Go 感兴趣,不妨跟着这些示例敲一遍代码,你会发现 Go 的简洁与高效确实让人着迷。


相关推荐
扉页的墨3 小时前
Wails v2 实战:用 Go 写桌面应用,从 0 到 1 构建一个本地笔记工具
go
我叫黑大帅4 小时前
从零实现一个完整 RAG 系统:基于 Eino 框架的检索增强生成实战
后端·面试·go
不会写DN1 天前
通过eino-ext如何正常indexer RAG?
网络·面试·go
donecoding1 天前
遗嘱、水管与抢救室:TS 切入 Go 的流程控制、接口与并发
javascript·typescript·go
扉页的墨1 天前
Go 协程泄漏排查实战:我是如何把线上内存从 500MB 压到 20MB 的
go
donecoding1 天前
对象模型与内存的“钥匙理论”:TS 切入的 Go 的结构体与指针
javascript·typescript·go
我叫黑大帅2 天前
通过eino-ext如何正常indexer RAG?
后端·面试·go
审判长烧鸡2 天前
GO闭包【4】“普通闭包”与“循环闭包”之间捕获的核心区别
go·捕获·闭包
审判长烧鸡2 天前
Go 闭包【1】基础
go·闭包