【Go语言学习系列30】并发编程(三):select语句

Go语言学习笔记—golang并发编程之channel 视频来源于B站Go语言基础进阶视频av56860636 文章为自己整理的学习笔记,侵权即删,谢谢支持! 阅读详情

📚 原创系列: “Go语言学习系列”

🔄 转载说明: 本文最初发布于"Gopher部落"微信公众号,经原作者授权转载。

🔗 关注原创: 欢迎扫描文末二维码,关注"Gopher部落"微信公众号获取第一手Go技术文章。

📑 Go语言学习系列导航

本文是【Go语言学习系列】的第30篇,当前位于第三阶段(进阶篇)

🚀 第三阶段:进阶篇
  1. 并发编程(一):goroutine基础
  2. 并发编程(二):channel基础
  3. 并发编程(三):select语句 👈 当前位置
  4. 并发编程(四):sync包
  5. 并发编程(五):并发模式
  6. 并发编程(六):原子操作与内存模型
  7. 数据库编程(一):SQL接口
  8. 数据库编程(二):ORM技术
  9. Web开发(一):路由与中间件
  10. Web开发(二):模板与静态资源
  11. Web开发(三):API开发
  12. Web开发(四):认证与授权
  13. Web开发(五):WebSocket
  14. 微服务(一):基础概念
  15. 微服务(二):gRPC入门
  16. 日志与监控
  17. 第三阶段项目实战:微服务聊天应用

📚 查看完整Go语言学习系列导航

📖 文章导读

在本文中,您将了解:

  • select语句的基本语法和工作原理
  • 如何使用select实现超时处理和非阻塞IO
  • 多个channel操作的同步处理
  • select在并发编程中的常见应用模式
  • 使用select避免goroutine泄漏和死锁的技巧

作为Go并发编程的核心组件之一,select语句让我们能够优雅地处理多个channel操作,实现超时控制、取消操作和非阻塞通信等关键功能。掌握select是构建健壮Go并发程序的必备技能。

Go select语句示意图

并发编程(三):select语句

在前两篇文章中,我们深入探讨了Go语言并发编程的基础:goroutine和channel。今天,我们将介绍Go并发编程的另一个强大工具:select语句。select语句允许一个goroutine等待多个通信操作,是Go语言中处理多channel操作的关键机制。

一、select基础

1.1 基本语法

select语句的语法类似于switch语句,但它的作用完全不同。select语句用于在多个发送/接收channel操作中进行选择,语法如下:

select {
case <-ch1:
    // 如果从ch1成功接收数据,则执行此分支
case ch2 <- value:
    // 如果成功向ch2发送数据,则执行此分支
case x := <-ch3:
    // 如果从ch3成功接收数据,则执行此分支,并将接收的值赋给x
default:
    // 如果上面的case都没有准备好,则执行此分支(可选)
}

1.2 工作原理

select语句的工作原理如下:

  1. 评估所有channel表达式:首先计算所有case中的channel表达式
  2. 评估所有发送/接收操作:尝试在所有channel上执行发送或接收操作
  3. 阻塞或执行
    • 如果有一个或多个case准备好(可发送或可接收),Go会随机选择一个执行
    • 如果没有case准备好且有default分支,则执行default分支
    • 如果没有case准备好且没有default分支,则select语句阻塞,直到某个case准备好

1.3 特性与规则

select语句有几个重要的特性:

  1. 随机选择:当多个case同时准备好时,select会随机选择一个执行,这避免了固定顺序可能导致的饥饿问题
  2. 零case:空的select语句(select{})会永远阻塞
  3. 无匹配死锁检测:如果select语句中没有default分支,且所有case都阻塞,则当前goroutine会被阻塞;如果所有goroutine都被阻塞,Go运行时会检测到死锁并报错
  4. default避免阻塞:包含default分支的select语句永远不会阻塞

1.4 基本示例

下面是一个简单的select示例,展示了如何在多个channel上等待:

package main

import (
    "fmt"
    "time"
)

func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)
    
    // 在两个不同的goroutine中发送数据
    go func() {
        time.Sleep(1 * time.Second)
        ch1 <- "from channel 1"
    }()
    
    go func() {
        time.Sleep(2 * time.Second)
        ch2 <- "from channel 2"
    }()
    
    // 使用select等待两个channel
    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-ch1:
            fmt.Println("Received", msg1)
        case msg2 := <-ch2:
            fmt.Println("Received", msg2)
        }
    }
}

在这个例子中,select语句会等待ch1或ch2有数据可接收。由于ch1会在1秒后接收到数据,而ch2在2秒后接收到数据,所以先打印"Received from channel 1",然后打印"Received from channel 2"。

二、超时处理

select语句结合time.After函数可以很方便地实现超时处理,这是Go并发编程中的一个常见模式。

2.1 基本超时模式

以下是一个基本的超时模式示例:

package main

import (
    "fmt"
    "time"
)

func main() {
    ch := make(chan string)
    
    go func() {
        // 模拟耗时操作
        time.Sleep(2 * time.Second)
        ch <- "操作完成"
    }()
    
    select {
    case result := <-ch:
        fmt.Println("成功:", result)
    case <-time.After(1 * time.Second):
        fmt.Println("操作超时")
    }
}

在这个例子中,如果在1秒内没有从ch接收到数据,则会执行超时分支。由于操作需要2秒,所以会打印"操作超时"。

2.2 超时重试模式

结合循环和超时,可以实现带重试的超时模式:

package main

import (
    "fmt"
    "time"
)

func main() {
    ch := make(chan string)
    
    go func() {
        // 模拟需要多次尝试才能成功的操作
        time.Sleep(2500 * time.Millisecond)
        ch <- "操作成功"
    }()
    
    timeout := 1 * time.Second
    maxRetries := 3
    
    for retry := 0; retry < maxRetries; retry++ {
        select {
        case result := <-ch:
            fmt.Println("成功:", result)
            return
        case <-time.After(timeout):
            fmt.Printf("尝试 %d 超时,重试中...\n", retry+1)
        }
    }
    
    fmt.Println("达到最大重试次数,操作失败")
}

在这个例子中,我们最多尝试3次,每次超时后会重试,直到成功或达到最大重试次数。

2.3 带取消功能的超时

结合context包,可以实现可取消的超时处理:

package main

import (
    "context"
    "fmt"
    "time"
)

func worker(ctx context.Context) <-chan string {
    resultCh := make(chan string)
    
    go func() {
        defer close(resultCh)
        
        // 模拟耗时操作
        for i := 0; i < 5; i++ {
            select {
            case <-ctx.Done():
                fmt.Println("Worker: 操作被取消")
                return
            case <-time.After(1 * time.Second):
                // 继续工作
                fmt.Println("Worker: 工作中...")
            }
        }
        
        resultCh <- "操作完成"
    }()
    
    return resultCh
}

func main() {
    // 创建可取消的context,超时时间为3秒
    ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
    defer cancel() // 确保所有路径都会取消context
    
    resultCh := worker(ctx)
    
    select {
    case result, ok := <-resultCh:
        if ok {
            fmt.Println("结果:", result)
        } else {
            fmt.Println("通道已关闭,没有结果")
        }
    case <-ctx.Done():
        fmt.Println("Main: 操作超时或被取消:", ctx.Err())
    }
}

在这个例子中,如果操作在3秒内没有完成,context会超时,worker会收到取消信号并退出。

三、非阻塞通信

select语句的另一个重要用途是实现非阻塞的channel操作。

3.1 非阻塞发送

通过select和default分支,可以实现非阻塞的发送操作:

package main

import "fmt"

func main() {
    ch := make(chan string)
    
    // 非阻塞发送
    select {
    case ch <- "hello":
        fmt.Println("发送成功")
    default:
        fmt.Println("发送失败,通道没有接收者")
    }
}

在这个例子中,由于ch是无缓冲channel且没有接收者,所以会执行default分支,打印"发送失败,通道没有接收者"。

3.2 非阻塞接收

类似地,可以实现非阻塞的接收操作:

package main

import "fmt"

func main() {
    ch := make(chan string)
    
    // 非阻塞接收
    select {
    case msg := <-ch:
        fmt.Println("接收成功:", msg)
    default:
        fmt.Println("没有数据可接收")
    }
}

这种模式在需要检查channel是否有数据但不想阻塞当前goroutine时非常有用。

3.3 实用案例:限时操作

结合非阻塞操作和超时,可以实现更复杂的业务逻辑:

package main

import (
    "fmt"
    "time"
)

func main() {
    ch := make(chan string)
    
    go func() {
        // 模拟数据源
        time.Sleep(2 * time.Second)
        ch <- "数据1"
        time.Sleep(1 * time.Second)
        ch <- "数据2"
        time.Sleep(1 * time.Second)
        ch <- "数据3"
        close(ch)
    }()
    
    // 每500毫秒尝试读取一次,超时3秒后退出
    timeout := time.After(3 * time.Second)
    tick := time.Tick(500 * time.Millisecond)
    
    for {
        select {
        case data, ok := <-ch:
            if !ok {
                fmt.Println("通道已关闭,退出")
                return
            }
            fmt.Println("接收到:", data)
        case <-tick:
            fmt.Println("tick - 检查是否有新数据")
        case <-timeout:
            fmt.Println("总体操作超时,退出")
            return
        }
    }
}

在这个例子中,我们组合使用了三种不同类型的channel操作:数据接收、定时检查和总体超时。

四、多路复用与优先级

select提供了一种同时等待多个channel的机制,但有时我们需要处理更复杂的场景,如实现优先级或组合多个select语句。

4.1 嵌套select实现优先级

通过嵌套select语句,可以实现channel操作的优先级:

package main

import (
    "fmt"
    "time"
)

func main() {
    highPriorityCh := make(chan string)
    lowPriorityCh := make(chan string)
    
    // 填充两个通道
    go func() {
        time.Sleep(500 * time.Millisecond)
        highPriorityCh <- "高优先级消息"
    }()
    
    go func() {
        time.Sleep(300 * time.Millisecond)
        lowPriorityCh <- "低优先级消息"
    }()
    
    // 实现优先级选择
    time.Sleep(600 * time.Millisecond) // 确保两个通道都有数据
    
    // 先检查高优先级通道
    select {
    case msg := <-highPriorityCh:
        fmt.Println("接收高优先级消息:", msg)
    default:
        // 如果高优先级通道没有消息,则检查低优先级通道
        select {
        case msg := <-lowPriorityCh:
            fmt.Println("接收低优先级消息:", msg)
        default:
            fmt.Println("没有消息可接收")
        }
    }
}

这个例子展示了如何使用嵌套select实现优先级。尽管低优先级通道更早有数据,但高优先级通道的消息会优先被处理。

4.2 循环select实现公平轮询

在需要公平处理多个channel的场景中,可以使用循环切换select的模式:

package main

import (
    "fmt"
    "time"
)

func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)
    ch3 := make(chan string)
    
    // 定期发送数据到三个通道
    go func() {
        tick := time.Tick(400 * time.Millisecond)
        for i := 0; ; i++ {
            <-tick
            select {
            case ch1 <- fmt.Sprintf("ch1: 消息 %d", i):
            case ch2 <- fmt.Sprintf("ch2: 消息 %d", i):
            case ch3 <- fmt.Sprintf("ch3: 消息 %d", i):
            }
        }
    }()
    
    // 公平轮询三个通道
    for i := 0; i < 10; i++ {
        select {
        case msg := <-ch1:
            fmt.Println(msg)
        case msg := <-ch2:
            fmt.Println(msg)
        case msg := <-ch3:
            fmt.Println(msg)
        }
    }
}

在这个例子中,我们通过一个select语句公平地轮询三个channel,确保每个channel都有机会被处理。

4.3 多select合并

有时候我们需要在多个goroutine中使用select,并将结果合并。可以使用fan-in模式:

package main

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

// 监听多个来源并合并输出
func fanIn(sources ...<-chan string) <-chan string {
    mergedCh := make(chan string)
    
    // 为每个输入通道启动一个goroutine
    for _, ch := range sources {
        go func(c <-chan string) {
            for v := range c {
                mergedCh <- v
            }
        }(ch)
    }
    
    return mergedCh
}

// 模拟一个数据源
func source(name string, interval time.Duration) <-chan string {
    ch := make(chan string)
    go func() {
        for i := 0; i < 3; i++ {
            time.Sleep(interval)
            ch <- fmt.Sprintf("来自%s的消息%d", name, i)
        }
        close(ch)
    }()
    return ch
}

func main() {
    // 创建三个不同的数据源
    source1 := source("源1", 300*time.Millisecond)
    source2 := source("源2", 500*time.Millisecond)
    source3 := source("源3", 200*time.Millisecond)
    
    // 合并所有数据源
    merged := fanIn(source1, source2, source3)
    
    // 接收合并后的消息
    for i := 0; i < 9; i++ { // 3个源,每个3条消息
        fmt.Println(<-merged)
    }
}

这个模式允许我们从多个来源并发地接收数据,而不必担心阻塞。

五、常见应用模式

select语句在Go并发编程中有许多常见的应用模式,这些模式帮助我们解决各种并发问题。

5.1 监控与取消

使用select监控多个任务并实现取消功能:

package main

import (
    "context"
    "fmt"
    "time"
)

// 监控任务的执行,支持取消操作
func monitorTask(ctx context.Context) {
    // 创建定时器
    heartbeat := time.Tick(1 * time.Second)
    taskDone := make(chan struct{})
    
    // 启动任务
    go func() {
        // 模拟耗时任务
        fmt.Println("任务开始执行...")
        time.Sleep(5 * time.Second)
        fmt.Println("任务完成")
        taskDone <- struct{}{}
    }()
    
    // 使用select监控任务执行情况
    for {
        select {
        case <-taskDone:
            fmt.Println("任务已完成,监控结束")
            return
        case <-heartbeat:
            fmt.Println("监控中: 任务正在执行...")
        case <-ctx.Done():
            fmt.Println("收到取消信号,监控结束:", ctx.Err())
            return
        }
    }
}

func main() {
    // 创建可取消的context
    ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
    defer cancel()
    
    monitorTask(ctx)
}

这个例子展示了如何使用select监控长时间运行的任务,同时实现了超时取消功能。

5.2 流水线模式

select可用于构建高效的流水线处理:

package main

import (
    "fmt"
    "time"
)

// 生成器 - 产生整数序列
func generator(done <-chan struct{}) <-chan int {
    outCh := make(chan int)
    
    go func() {
        defer close(outCh)
        for i := 0; ; i++ {
            select {
            case <-done:
                return
            case outCh <- i:
                time.Sleep(100 * time.Millisecond)
            }
        }
    }()
    
    return outCh
}

// 处理器 - 将输入值翻倍
func doubler(done <-chan struct{}, inCh <-chan int) <-chan int {
    outCh := make(chan int)
    
    go func() {
        defer close(outCh)
        for v := range inCh {
            select {
            case <-done:
                return
            case outCh <- v * 2:
            }
        }
    }()
    
    return outCh
}

// 过滤器 - 只保留偶数
func filter(done <-chan struct{}, inCh <-chan int) <-chan int {
    outCh := make(chan int)
    
    go func() {
        defer close(outCh)
        for v := range inCh {
            if v%2 == 0 {
                select {
                case <-done:
                    return
                case outCh <- v:
                }
            }
        }
    }()
    
    return outCh
}

func main() {
    done := make(chan struct{})
    
    // 创建处理流水线
    values := generator(done)
    doubled := doubler(done, values)
    filtered := filter(done, doubled)
    
    // 处理结果
    for i := 0; i < 5; i++ {
        fmt.Println(<-filtered)
    }
    
    // 完成后发送取消信号
    close(done)
    time.Sleep(100 * time.Millisecond) // 给goroutine一点时间清理
    fmt.Println("程序完成")
}

这种流水线模式允许我们构建灵活的数据处理管道,每个阶段都可以并发执行,同时通过取消信号保持可控性。

六、常见错误与最佳实践

使用select语句时常见的错误包括:

  1. 忘记处理超时:在需要超时控制的场景中忘记添加超时case

    // 错误示例 - 没有超时控制
    select {
    case data := <-ch:
        process(data)
    }
    // 如果ch没有数据,这里会永久阻塞
    
  2. 在循环中重复创建time.After:可能导致内存泄漏

    // 错误示例 - 每次循环都创建新的timer
    for {
        select {
        case data := <-ch:
            process(data)
        case <-time.After(1 * time.Second): // 每次循环都创建新的timer!
            fmt.Println("超时")
        }
    }
    
  3. 忽略channel关闭:没有正确处理channel关闭的情况

    // 错误示例 - 没有检查channel是否关闭
    select {
    case data := <-ch:
        process(data)
    }
    // 如果ch已关闭,这里会立即返回零值,可能导致逻辑错误
    
  4. 死锁:所有case都阻塞且没有default分支

    // 错误示例 - 可能导致死锁
    ch1 := make(chan int)
    ch2 := make(chan int)
    select {
    case <-ch1: // 阻塞
    case <-ch2: // 阻塞
    }
    // 如果没有任何goroutine向ch1或ch2发送数据,这里会死锁
    

最佳实践:

  1. 总是包含超时或取消机制:避免永久阻塞

    // 推荐方式 - 包含超时
    select {
    case data := <-ch:
        process(data)
    case <-time.After(5 * time.Second):
        return errors.New("操作超时")
    }
    
  2. 重用timer:在循环中使用select时,重用timer而不是每次都创建新的

    // 推荐方式 - 重用timer
    timer := time.NewTimer(1 * time.Second)
    for {
        timer.Reset(1 * time.Second)
        select {
        case data := <-ch:
            process(data)
        case <-timer.C:
            fmt.Println("超时")
        }
    }
    
  3. 检查channel关闭:使用接收操作的第二个返回值检查channel是否已关闭

    // 推荐方式 - 检查channel关闭
    select {
    case data, ok := <-ch:
        if !ok {
            fmt.Println("channel已关闭")
            return
        }
        process(data)
    case <-time.After(1 * time.Second):
        fmt.Println("超时")
    }
    
  4. 使用default避免阻塞:在不需要阻塞的场景,使用default分支

    // 推荐方式 - 使用default避免阻塞
    select {
    case data := <-ch:
        process(data)
    default:
        // 无数据时不阻塞,执行其他操作
    }
    
  5. 结合context进行取消控制:使用context包实现更灵活的取消机制

    // 推荐方式 - 使用context
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()
    
    select {
    case data := <-ch:
        process(data)
    case <-ctx.Done():
        fmt.Println("操作被取消或超时:", ctx.Err())
    }
    

👨‍💻 关于作者与Gopher部落

"Gopher部落"专注于Go语言技术分享,提供从入门到精通的完整学习路线。

🌟 为什么关注我们?

  1. 系统化学习路径:本系列56篇文章循序渐进,带你完整掌握Go开发
  2. 实战驱动教学:理论结合实践,每篇文章都有可操作的代码示例
  3. 持续更新内容:定期分享最新Go生态技术动态与大厂实践经验
  4. 专业技术社区:加入我们的技术交流群,与众多Go开发者共同成长

📱 关注方式

  1. 微信公众号:搜索 “Gopher部落”“GopherTribe”
  2. CSDN专栏:点击页面右上角"关注"按钮

💡 读者福利

关注公众号回复 “Go学习” 即可获取:

  • 完整Go学习路线图
  • Go面试题大全PDF
  • Go项目实战源码
  • 定制学习计划指导

期待与您在Go语言的学习旅程中共同成长!

GO语言--条件语句select 如果没有任意一条语句可以执行(即所有的通道都被阻塞),那么有两种可能的情况: ①如果给出了default语句,那么就会执行default的流程,同时程序的执行会从select语句后的语句中恢复。select的用法与switch语法非常类似,由select开始的一个新的选择块,每个选择条件由case语句来描述。如果没有可运行的case语句,且没有default语句select将阻塞,直到某个case通信可以运行。如果没有可运行的case语句,且有default语句,那么就会执行default的动作。 阅读详情

相关推荐

golang select default continue_15. 理解 Go 语言中的 select 用法

大家好,我是明哥。由于某些不可抗力,之前该专栏的所有内容都被删除了。因此之后的一段时间内,我会重新发布这些文章,先给已经看过的关注者提个醒。本文原文:http://golang.iswbm.comGithub:http://github.com/iswbm/GolangCodingTime前面写过两节关于 switch-case 的文章,分别是:流程控制:switch-caseGo 语言中的类型断...

weixin_40004659的博客 707

Go语言Select实现原理

一、select简介 1、Goselect语句是一种仅能用于channl发送和接收消息的专用语句,此语句运行期间是阻塞的;当select中没有case语句的时候,会阻塞当前groutine。 2、selectGolang语言层面提供的I/O多路复用的机制,其专门用来检测多个channel是否准备完毕:可读或可写。 3、select语句中除default外,每个case操作一个channel,要么读要么写 4、select语句中除d...

专注基础架构领域 3950

Go 语言 select 语句详解

Go语言select语句并发编程的核心机制,主要用于同时监听多个通道操作。文章详细解析了select的10种典型应用场景:包括多通道接收、超时控制、请求处理、优先级调度、非阻塞操作等实用案例。通过示例代码展示了如何实现通道状态监控、任务调度、优雅退出等功能。文章还总结了select的重要特性(随机性、nil通道处理)和使用注意事项,并列举了API网关、实时系统等实际应用场景。掌握select能有效提升Go并发程序的灵活性和响应能力。

qq_44805265的博客 964

Go语言中的Select:深度解析与实战案例

select是操作系统中的系统调用,我们以前在学校中学习操作系统课程或者在工作当中,肯定都使用过或者了解过selectpoll和epoll等函数构建 I/O 多路复用模型提升程序的性能。Go 语言select与操作系统中的select很相似,今天这篇文章会深度解析 Go 语言select关键字。在Go语言中,select语句用于处理多个通信操作,如通道操作。它允许我们等待多个操作完成,并根据条件执行相应的代码块。select语句并发编程中非常有用,特别是当我们需要处理多个通道操作时。select

知道自己不要什么,不管多么大的诱惑(欢迎关注:职谷智享) 1974

GO语言学习(14)GO并发编程

GO语言并发编程介绍

m0_61990598的博客 1557

go语言学习--3.常用语句

在多重循环中,可以用标号 label 标出想 continue 的循环。如果没有 case 可运行,它将阻塞,直到有 case 可运行。break主要用于循环语句跳出循环,和c语言中的使用方式是相同的。且在多重循环的时候还可以使用label标出想要break的循环。selectGo 中的一个控制结构,类似于用于通信的 switch 语句。可以是同类型的任意值,类型不局限为常量或者整数,或者最终结果为相同类型的表达式。语句,可以实现并发代码中的异步通信和超时处理,以及其他复杂的并发模式。

我是个好人呀,O(∩_∩)O 959

Go语言学习[13]_并发编程

Go语言学习 道法自然 文章目录Go语言学习前言一、并发编程1.1 并发与并行1.2 为什么需要并发?1.3 Go并发原语1.3.1协程Goroutines1.3.2协程Goroutines总结 前言 Go(又称Golang)是Google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言Go语言语法简单,包含了类C语法。快速的编译时间,开发效率和运行效率高。组合的思想、无侵入式的接口。 参考链接 一、并发编程 并发使并行变得容易,并发提供了一种构造解决方案的方法,并行一般

牛博通的博客 304

Go语言学习笔记(一)

Go语言学习整理 本文基于菜鸟教程,对于自己不明白的点加了点个人注解,对于已明确的点做了删除,可能结构不太清晰,看官们可移步Go语言教程 1 Go语言结构 当标识符(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,如:Group1,那么使用这种形式的标识符的对象就可以被外部包的代码所使用(客户端程序需要先导入这个包),这被称为导出(像面向对象语言中的 public);标识...

暗焰之珩的博客 1万+

Go语言学习系列34】数据库编程(一):SQL接口

详细介绍Go语言标准库中的database/sql包,包括数据库连接、查询操作、事务处理、NULL值处理等,帮助你构建可靠的数据库应用程序

GopherTribe的博客 1057

Go语言条件语句

Go语言条件语句 GO语言学习教程 www.lsdcloud.com 1 程序的流程结构 程序的流程控制结构一共有种:顺序结构,选择结构,循环结构。 顺序结构:从上向下,逐行执行。 选择结构:条件满足,某些代码才会执行。0-1次 分支语句:if,switch,select 循环结构:条件满足,某些代码会被反复的执行多次。0-N次 循环语句:for 2 条件语句 2.1 If语句 语法格式:...

ourLang 2万+

golangGo语言学习-select用法

golangselect 的功能和 select, poll, epoll 相似, 就是监听 IO 操作,当 IO 操作发生时,触发相应的动作。 示例: ch1 := make (chan int, 1) ch2 := make (chan int, 1) ... select { case <-ch1: fmt.Println("ch1 pop one elemen

zhonglinzhang 2万+

Go语言学习之旅--并发编程

https://blog.csdn.net/guolianggsta/article/details/123608249?spm=1001.2014.3001.5501https://blog.csdn.net/guolianggsta/article/details/123608266?spm=1001.2014.3001.5501https://blog.csdn.net/guolianggsta/article/details/123608284?spm=1001.2014.3001.5501http

qq_45661125的博客 640

GO语言学习(6)条件语句

本文将主要介绍if...else语句,switch语句以及select语句种条件语句的使用。

m0_61990598的博客 924

go语言学习第五天==>并发编程go关键词,并发通信channel、select关键字、缓冲机制、socket编程

go语言学习第五天 - 并 发 编 程 goroutine是Go语言中的轻量级线程实现 go关键词 在一个函调用前加上go关键字, 这次调用就会在一个新的goroutine中并发执行,当函数返回则结束,如果这个函数有返回值则会被丢弃 demo 当mian函数结束时程序就退出,即终止所有goroutine func Add(x, y int) { z := x + y fmt.Pri...

KongYu01的博客 529

Datawhale go语言学习 12并发编程

12.并发编程 12.1 并发与并行 Erlang 之父 Joe Armstrong曾经以下图解释并发与并行。 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u21Pi0Ef-1608215197628)(./img/cor.jpg)] 并发在图中的解释是两队人排队接咖啡,两队切换。 并行是两个咖啡机,两队人同时接咖啡。 “Concurrency is about dealing with lots of things at once. Parallelism is abou

Andrew_zjc的博客 361

Go语言学习之旅 4 - Go 的并发原语

Go语言学习笔记 3 - Go 的并发原语 概述 连续节的内容如下: 第一节覆盖了基本语法及数据结构 第二节讨论了方法与接口 第节则简单介绍了 Go 的并发原语。 GoGo 程(goroutine)是由 Go 运行时管理的轻量级线程。 go 函数名 会启动一个新的 Go 程并执行这个函数。 比如: go say("world") // 将启动一个线程来执行 say 函...

张云飞Vir的专栏 512

Go语言学习——8、条件、循环语句

一、条件语句 Go 语言提供了以下几种条件判断语句: 注意:Go 没有目运算符(条件运算符),所以不支持“表达式b ? x : y”形式的条件判断。 1.1、 if 语句 package main import "fmt" func main() { /* 定义局部变量 */ var a int = 10 /* 使用 if 语句判断布尔表达式 */ if a...

李骁睿写技术的地方 293

Go语言学习】——并发编程

并发编程

Kaychenl的博客 649
上一篇: 【Go语言学习系列29】并发编程(二):channel基础
下一篇: 【Go语言学习系列32】并发编程(五):并发模式
Gopher部落
博客等级 码龄1年 784粉丝 92原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Gopher部落

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值