首页 > 编程语言 >golang并发编程

golang并发编程

时间:2023-08-02 12:23:23浏览次数:31  
标签:协程 fmt 编程 chan golang 并发 func var main

23 协程(Goroutine)

  • 定义:协程(goroutine)是 Go 语言中的一种轻量级线程,可以在单个线程中同时执行多个任务。

  • 使用方法:在调用函数时go function()

  • 在函数中使用协程时,需要注意以下几点:

    1. 协程的执行是异步的,因此需要使用通道等方式进行同步。

    2. 协程共享内存空间,因此需要使用互斥锁等方式进行同步,避免竞态条件(race condition)的出现。

    3. 协程的数量应该根据系统的CPU核心数量和任务的性质等因素进行合理的调度,避免过多的协程导致系统资源的浪费和竞争等问题。

示例1:

package main

import "fmt"

func test()  {
	fmt.Println("hello, world")
}

func main() {
	go test()
}

WaitGroup

  • 定义:WaitGroup是Go语言标准库中的一个结构体,它提供了一种简单的机制,用于同步多个协程的执行。适用于需要并发执行多个任务并等待它们全部完成后才能继续执行后续操作的场景。

  • 首先主协程创建WaitGroup实例,然后在每个协程的开始处,调用Add(1)方法,表示需要等待一个任务执行完成,然后协程在任务执行完成之后,调用Done方法,表示任务已经执行完成了。

    主协程中,需要调用Wait()方法,等待所有协程完成任务,示例如下:

    示例1:

    func main(){
        //首先主协程创建WaitGroup实例
        var wg sync.WaitGroup
        // 开始时调用Add方法表示有个任务开始执行
        wg.Add(1)
        go func() {
            // 开始执行...
            //完成之后,调用Done方法
            wg.Done()
        }()
        // 调用Wait()方法,等待所有协程完成任务
        wg.Wait()
        // 执行后续逻辑
    }
    

协程安全

  • 定义:协程安全是指在并发编程中,使用协程时不会出现数据竞争等问题。Go语言中的协程是通过goroutine实现的,而goroutine是轻量级的线程,因此协程之间的调度和切换都是由操作系统自动完成的。

  • 非常经典的例子,两个协程函数,分别对同一个全局变量进行操作,按照我们预期的结果,应该是200万,但是多运行几次,会发现结果各不相同。

    这就是协程安全问题

示例1:

package main

import (
    "fmt"
    "sync"
)

var w = sync.WaitGroup{}
var num = 0

func AddNum() {
    for i := 0; i < 1000000; i++ {
        num++
    }
    w.Done()
}

func main() {
    w.Add(2)
    go AddNum()
    go AddNum()
    w.Wait()
    fmt.Println(num)

}

  • 这种情况我们可以通过加锁进行解决,go语言中给我们通过了这个方法

  • 互斥锁(Mutex):Go语言提供了互斥锁机制来保护共享资源的访问。互斥锁可以通过Lock()和Unlock()方法实现资源的互斥访问,从而避免多个协程同时访问同一个资源的问题。

示例2:

package main

import (
    "fmt"
    "sync"
)

var lock = sync.Mutex{}
var w = sync.WaitGroup{}
var num = 0

func AddNum() {
    // 上锁之后其他协程就进不来
    lock.Lock()  // 上锁
    for i := 0; i < 1000000; i++ {
        num++
    }
    // 解锁之后,其他协程进入
    lock.Unlock() // 解锁
    w.Done() 
}

func main() {
    w.Add(2)
    go AddNum()
    go AddNum()
    w.Wait()

    fmt.Println(num)
}

获取协程返回值

  • 使用全局变量接收

示例1:

package main

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

var code string
var wg sync.WaitGroup

func main() {
    wg.Add(1)
	// 协程调用生成发送验证码函数,让主线程继续运行
	go sendCode()
    wg.Wait()
	// 用户输入
	userCode := userInput()
	// 判断验证码是否正确
	isRightCode(userCode)
}

// 用户输入
func userInput() string {
	fmt.Print("请输入验证码:")
	var userCode string
	for {
		_, err := fmt.Scanln(&userCode)
		if err != nil {
			fmt.Println("输入错误")
		} else {
			break
		}
	}
	return userCode
}

// 发送验证码
func sendCode() {
	for i := 3; i >= 1; i-- {
		fmt.Println("还剩", i, "秒!")
		time.Sleep(time.Second)
	}
	rand.Seed(time.Now().UnixNano())
	for i := 0; i < 6; i++ {
		r := rand.Intn(10)
		code += strconv.Itoa(r)
	}
	fmt.Println("验证码已发送")
	fmt.Println("验证码:", code)
    wg.Done()
}

// 判断验证码是否正确
func isRightCode(userCode string) {
	if userCode == code {
		fmt.Println("验证码正确!")
	} else {
		fmt.Println("验证码错误!")
	}
}

24 信道(channel)

  • 定义:Channel 是 Go 语言中的一个重要概念,它是一个类似于管道的数据结构,用于在不同的 goroutine 之间传递数据。Channel 可以看作是一种同步机制,它可以帮助我们实现 goroutine 之间的通信和数据交换。
  • 存入:channel <- value
    取出:value, ok <- channel
    丢弃:<- channel
    • 先进先出,自动阻塞
    • 数据需要保持流动,否则会阻死报错

示例1:

package main

import (
 "fmt"
)

func main() {
 // 创建一个带缓冲区的字符串类型的通道,缓冲区大小为3
 ch := make(chan string, 3)
 
 // 向通道中发送数据
 ch <- "123"
 ch <- "456"
 ch <- "789"

 // 从通道中接收数据并打印
 s := <-ch
 fmt.Println(s)  // 123
 fmt.Println(<-ch)  // 456

 // 从通道中接收数据并检查是否还有数据可用,然后打印结果和ok的值
 ss, ok := <-ch
 fmt.Println(ss, ok)  // 789 true

 // 关闭通道,不再接收数据
 close(ch)
}

搭配协程

示例2:

package main

import "fmt"

func pushNum(c chan int) {
	for i := 0; i < 100; i++ {
		c <- i
	}
	close(c) // 写完必须要关闭,不然会死锁
}

func main() {
	c1 := make(chan int, 2) // 2表示缓冲区大小
	go pushNum(c1)

	for value := range c1 {
		fmt.Println(value)
	}
}

  • 多个协程函数,close就不能写在协程函数里了

示例3:

package main

import (
	"fmt"
	"sync"
)

var ch chan int = make(chan int, 10)
var wg = sync.WaitGroup{}

func pushNum() {
	for i := 0; i < 5; i++ {
		ch <- i
	}
	wg.Done()
}

func main() {
	wg.Add(2)
	go pushNum()
	go pushNum()
	wg.Wait()
	close(ch)
	for {
		res, ok := <-ch
		if !ok {
			break
		}
		fmt.Println(res)
	}
}

close

  • 使用close之后就不能在继续写入了,但是还可以继续从缓冲区读取

    1. close之后,读取的chan是数据类型的默认值

    2. close之后,不能再往chan里面写入数据

    3. for range之前必须要close

  • 可读可写

    • 将一个读写通道 ch 转换为两个单向通道 readChwriteCh

示例4:

package main

import "fmt"

func main() {
    var ch chan int = make(chan int, 2)
    // 可读chan
    var readCh <-chan int = ch
    // 可写chan
    var writeCh chan<- int = ch

    writeCh <- 1
    writeCh <- 2

    fmt.Println(<-readCh)
    fmt.Println(<-readCh)

}

select...case

  • 适用于无法确认合适关闭信道的情况
  • 通常结合for循环使用
  • select ... case会阻塞到某个分支可以继续执行时执行该分支,当没有可执行的分支是执行default分支

示例5:

package main

import "fmt"

func main() {
    var ch1 chan int = make(chan int, 2)
    var ch2 chan int = make(chan int, 2)
    var ch3 chan int = make(chan int, 2)
    ch1 <- 1
    ch2 <- 2
    ch3 <- 3

    select {
        // 监听多个chan的情况,是随机执行
        case v := <-ch1:
        fmt.Println(v)
        case v := <-ch2:
        fmt.Println(v)
        case v := <-ch3:
        fmt.Println(v)
        default:
        fmt.Println("没有数据")
    }
}

标签:协程,fmt,编程,chan,golang,并发,func,var,main
From: https://www.cnblogs.com/xiufanivan/p/17600345.html

相关文章

  • Golang基础
    1、GOLANG概述1.1语言特点1.2开发工具1.3相关文档官方编程指南标准库API文档2、规范的代码风格&注释2.1注释&注释风格2.2缩进&空白3、编码4、数据类型4.1基本数据库类型变量常量整型浮点型字符串布尔类型指针基本数据类型零值4.2复合数据类型数......
  • Visual Studio下载_VS编程开发工具Visual Studio官方版 软件推荐
    VisualStudio2019软件特色一、使用VSTS加快从构想到发布的进程1、CI/CD使用高性能管道以闪电般的速度测试代码并将其部署到生产。根据需要开始处理小型任务和纵向扩展。2、Agile开始按自己的方式实现敏捷方法。VSTS提供可配置看板、交互式积压工作(backlog)、简单易用的计划......
  • 2023钉耙编程 day4
    NumberTable在\(2\)行\(n\)列的矩阵中,计算满足矩阵内所有数组异或和为\(0\)每一行、每一列数字互异每个数的取值范围为\([0,2^k)\)的填数方案数题意相当于每行内有\(\dfrac{n(n-1)}{2}\)对不等关系的限制,每列内有\(1\)对不等关系的限制,总共\(n^2\)对不......
  • Java面试题 P35:数据库篇:MySql篇-事务-并发事务带来哪些问题?怎么解决这些问题呢?MySQL
         ......
  • [golang]使用tail追踪文件变更
    简介借助github.com/hpcloud/tail,可以实时追踪文件变更,达到类似shell命令tail-f的效果。示例代码以下示例代码用于实时读取nginx的access.log日志文件,读取到后输出到控制台。如果nginx日志做了json格式化,还可以解析读取到的内容,对日志进行更多处理,比如日志内容写入数据库、......
  • 电商API接口对接(商品详情,评论,按图搜图,订单列表)代码封装,可高并发
    淘宝API接口对接需要以下步骤:申请淘宝开放平台账号:在淘宝开放平台(注册账号,并创建自己的应用)选择API接口:根据需要的功能,选择相应的API接口,例如商品API接口、店铺API接口、订单API接口等。获取授权:使用OAuth2.0授权方式,获取用户或店铺的授权,才能够访问和调用API接口。调用API接口:按照......
  • Flask的线程,携程与并发 (2)
    Flask的线程,携程与并发(2)pipreqs:-项目依赖pip3installpipreqs-生成依赖文件:pipreqs./-安装依赖文件:pip3install-rrequirements.txt函数和方法fromtypesimportMethodType,FunctionTypeclassFoo(object): deffetch(self): passprint(isinstance(......
  • Flask的线程,携程与并发
    Flask的线程,携程与并发并发编程#1操作系统发展史#2进程基础:操作系统上运行的程序,是资源分配的最小单位#3进程调度:时间片轮转法#4并发和并行#5同步,异步,阻塞,非阻塞#6python创建进程 -两种方式: -类继承:Process,重写run方法-Process(target=任务)......
  • 什么是并发,高并发,高并发下调用商品详情API数据
    并发是指在一个时间段内有多个进程在执行。什么是高并发定义:高并发(HighConcurrency)是使用技术手段使系统可以并行处理很多请求。关键指标:-响应时间(ResponseTime)-吞吐量(Throughput)-每秒查询率QPS(QueryPerSecond)-每秒事务处理量TPS(TransactionPerSecond)-同时在线用......
  • VScode 中golang 单元测试,解决单元测试超时timeout30s
    目的:单元测试的主要目的是验证代码的每个单元(函数、方法)是否按照预期工作。提示:解决单元测试超时30s的问题在序号4 1准备以_test.go结尾文件和导入testing包在命名文件时需要让文件必须以_test结尾,在文件中导入testing包。单元测试源码文件可以由多个测试用例组成,每个测试......