go多线程

1.仅加go

在一个golang编写的程序,主函数运行完毕后,程序就结束了

package main

import (
    "fmt"
    "time"
)

func main() {
    // 如果这样写go 要加在上面的函数,因为如果只单独加在下面的函数或者都加上,程序就会直接仅执行上面的函数或者不执行
    go count(5, "羊")
    count(5, "牛")
}

func count(n int, animal string) {
    for i := 0; i < n; i++ {
       fmt.Println(i+1, animal)
       time.Sleep(time.Millisecond * 500)
    }
}

2.使得两个go都执行

1.直接time.Sleep(3 * time.Second)

package main

import (
	"fmt"
	"time"
)

func main()// 如果这样写go 要加在上面的函数,因为如果只单独加在下面的函数或者都加上,程序就会直接仅执行上面的函数或者不执行
	go count(5, "羊")
	go count(5, "牛")
	time.Sleep(3 * time.Second)
}

func count(n int, animal string) {
	for i := 0; i < n; i++ {
		fmt.Println(i+1, animal)
		time.Sleep(time.Millisecond * 500)
	}
}

2.WaitGroup 计数器 执行完一个任务就减1

追踪还有多少任务没有执行

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    var wg sync.WaitGroup
    wg.Add(2)
    go func() {
       count(5, "羊")
       wg.Done()
    }()
    go func() {
       count(5, "牛")
       wg.Done()
    }()
    wg.Wait() // 函数结束前原地听命,计数器变为0才继续
}

func count(n int, animal string) {
    for i := 0; i < n; i++ {
       fmt.Println(i+1, animal)
       time.Sleep(time.Millisecond * 500)
    }
}

3.交流goroutine

其他具有多线程的编程语言中,线程之间的交流通过共享内存完成

1.通过计数交流,counte 缺点是可能分配在不同的核心 可能同时执行

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    var wg sync.WaitGroup
    wg.Add(2)
    go func() {
       count(5, "羊")
       wg.Done()
    }()
    go func() {
       count(5, "牛")
       wg.Done()
    }()
    wg.Wait() // 函数结束前原地听命,计数器变为0才继续
    fmt.Println(counte)
}

var counte int

func count(n int, animal string) {
    for i := 0; i < n; i++ {
       fmt.Println(i+1, animal)
       counte++
       time.Sleep(time.Millisecond * 500)
    }
}

2.Channel

不通过共享内存交流,通过交流去共享内存

往channel发送和接收一条消息,都会阻塞代码的执行

当我要发送一条消息,我会一直在这里等着,直到一条消息在channel另一方被接收了

反过来,如果我想从channel接受一条消息 我会等着

如果channel另一方有一个人拿着消息等着,我会立刻收到消息,他会立刻发送消息,然后都会运行

可以通过阻塞的特性同步我们的代码

package main

import (
    "fmt"
    "time"
)

func main() {
    c := make(chan string)
    go count(5, "羊", c)
    for { // 消息有很多 使用for循环
       message := <-c // 从channel收到消息
       fmt.Println(message)
    }

}

func count(n int, animal string, c chan string) {
    for i := 0; i < n; i++ {
       // 直接往channel里喊话
       c <- animal
       time.Sleep(time.Millisecond * 500)
    }
}

主函数一直等待channel传来消息,但是count函数已经结束了

对程序来说,新的数据不会传进来

go检测到这一点,报错

在这里插入图片描述
在这里插入图片描述

package main

import (
    "fmt"
    "time"
)

func main() {
    c := make(chan string)
    go count(5, "羊", c)
    // 方式1
    for { // 消息有很多 使用for循环
       message, open := <-c // 从channel收到消息 会额外获得一个bool值,使用open接收
       if !open {           // 通过bool判断是不是要继续从channel接收消息
          break
       }
       fmt.Println(message)
    }

    // 方式2
    for message2 := range c {
       fmt.Println(message2)
    }

}

func count(n int, animal string, c chan string) {
    for i := 0; i < n; i++ {
       // 直接往channel里喊话
       c <- animal
       time.Sleep(time.Millisecond * 500)
    }
    close(c) // 关闭channel
}

3.从多个channel接收数据

当你把一个任务分为多个子任务时

可能和每一个子任务都有一个单独的channel专门和那一个子任务通讯

这些channel随时可能传来数据,怎么去管理

往channel发送和接收一条数据都会阻塞程序

有多个channel发送消息时

使用channel保证第一时间从有新消息的channel接收数据

不受不同channel消息频率不同的影响

package main

import (
    "fmt"
    "time"
)

func main() {
    c1 := make(chan string)
    c2 := make(chan string)

    go func() {
       for {
          c1 <- "羊"
          time.Sleep(time.Millisecond * 500)
       }
    }()

    go func() {
       for {
          c1 <- "牛"
          time.Sleep(time.Millisecond * 2000)
       }
    }()

    //for {
    // fmt.Println(<-c1) // 往channel发送和接收一条数据都会阻塞程序 所以c1执行完之后就会到c2,然后就会阻塞,c1的消息此时无人接收,所以使用select方法
    // fmt.Println(<-c2)
    //}

    for {
       select { // 有很多case 哪个不阻塞就先执行
       case msg := <-c1:
          fmt.Println(msg)
       case msg := <-c2:
          fmt.Println(msg)
       }

    }
}

4.树形结构便利 搜索文件

搜索所有叫test的文件或者文件夹

深度优先的搜索实现

1.直接遍历

package main

import (
    "fmt"
    "io/ioutil"
    "time"
)

var query = "test"
var matches int // 计量数字

var workerCount = 0
var maxWorkerCount = 32
var searchRequest = make(chan string) // 让包工头指派工作
var workerDone = make(chan bool)      // 让工人们互相告诉包工头说工作做完了
var foundMatch = make(chan bool)      // 传输关于我找到搜索的结果的消息

func main() {
    start := time.Now()
    workerCount = 1
    go search("/Users/lfzxmw/", true)
    waitForWorkers() //指派完第一个search等待一下
    fmt.Print(matches, "matches")
    fmt.Println(time.Since(start))
}

func waitForWorkers() {
    for {
       select {
       case path := <-searchRequest: // 有新的工作 search request有新的消息 把此时工人数量加一
          workerCount++
          go search(path, true) // 指派一个新的工人工作
       case <-workerDone: // 有工人工作结束 数量就减一
          workerCount--
          if workerCount == 0 { // 工作都做完了
             return // 退出等待的函数
          }
       case <-foundMatch: // 有新的结果找到
          matches++
       }

    }
}

func search(path string, master bool) {
    files, err := ioutil.ReadDir(path)
    if err == nil {
       for _, file := range files {
          name := file.Name()
          if name == query {
             foundMatch <- true
          }
          if file.IsDir() {
             if workerCount < maxWorkerCount {
                searchRequest <- path + name + "/"
             } else {
                search(path+name+"/", false)
             }

          }
       }
    }
    if master { // 看一下当前的搜索函数是不是一个在goroutine运行的搜索函数
       workerDone <- true // 是的话告诉总部工作结束
    }
}

5.扩展

1.带有buffer(缓存)的channel

2.go语言的context包裹

1.带有buffer(缓存)的channel

在 Go 语言中,带有缓冲的通道(buffered channel)允许你在通道中存储一定数量的数据,而不需要立即接收。这使得发送者在发送数据时可以继续工作,而不必等到接收者准备好接收。带缓冲的通道在需要在 goroutine 之间异步通信时非常有用。

package main

import (
	"fmt"
	"time"
)

func main() {
	// 创建一个缓冲区大小为 3 的通道
	bufferedChannel := make(chan int, 3)

	// 启动一个 goroutine 发送数据
	go func() {
		for i := 1; i <= 5; i++ {
			fmt.Printf("Sending %d to the channel\n", i)
			bufferedChannel <- i // 发送数据到通道
			fmt.Printf("Sent %d to the channel\n", i)
			time.Sleep(1 * time.Second) // 模拟工作延迟
		}
		close(bufferedChannel) // 发送完成后关闭通道
	}()

	// 接收数据
	for value := range bufferedChannel {
		fmt.Printf("Received %d from the channel\n", value)
		time.Sleep(2 * time.Second) // 模拟处理数据的延迟
	}

	fmt.Println("All values received from the channel.")
}

代码说明
  1. 创建缓冲通道
    • 使用 make(chan int, 3) 创建一个整型的缓冲通道,缓冲大小为 3。
  2. 发送数据的 goroutine
    • 在一个 goroutine 中,循环发送 5 个整数到通道中。由于通道是缓冲的,所以最多可以容纳 3 个整数而不会阻塞发送。
  3. 接收数据
    • 使用 for range 循环接收通道中的数据。接收者会阻塞,直到通道中有数据可用。
  4. 关闭通道
    • 发送完成后,使用 close(bufferedChannel) 关闭通道。这是一个好习惯,可以避免接收者在通道中等待更多数据。
注意事项
  • 如果缓冲通道已满,发送者将被阻塞,直到接收者读取数据,使得通道中有空位。
  • 如果通道已空,接收者将被阻塞,直到有数据可接收。
  • 关闭通道是必要的,特别是在需要通知接收者发送完成时。
总结

带缓冲的通道可以有效地在 goroutine 之间传递消息,允许更灵活的并发设计。它们非常适合需要一定程度的异步处理的场景。希望这个示例能够帮助您理解带缓冲通道的使用!如果您有任何进一步的问题,欢迎随时提问。

6.go语言的context包裹

在 Go 语言中,context 包是一个用于处理上下文(context)信息的标准库,广泛用于处理并发操作中的请求传递、取消信号以及超时控制。它尤其在编写网络服务和分布式系统时非常有用。

主要功能

  1. 请求的传递:可以在多个 goroutine 之间传递请求范围的值。
  2. 取消信号:允许上下文的持有者通知其他 goroutine 取消操作。
  3. 超时控制:可以设置超时,从而避免操作长时间阻塞。

常用类型

  • Context:接口类型,表示上下文。
  • Background:返回一个非空的 Context,通常用于顶层的 context。
  • TODO:返回一个非空的 Context,用于某些还未决定的上下文。

创建 Context

1. 背景上下文
ctx := context.Background()
2. 带取消的上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel() // 确保在不再需要时调用取消
3. 带超时的上下文
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel() // 确保在不再需要时调用取消
4. 带截止时间的上下文
deadline := time.Now().Add(5 * time.Second)
ctx, cancel := context.WithDeadline(context.Background(), deadline)
defer cancel() // 确保在不再需要时调用取消
5. 带值的上下文
ctx = context.WithValue(context.Background(), "key", "value")
value := ctx.Value("key")

示例代码

下面是一个完整的示例,展示如何使用 context 包处理并发操作和取消信号:

package main

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

func worker(ctx context.Context, id int) {
	for {
		select {
		case <-ctx.Done():
			fmt.Printf("Worker %d: stopped\n", id)
			return // 处理取消信号
		default:
			fmt.Printf("Worker %d: working...\n", id)
			time.Sleep(1 * time.Second) // 模拟工作
		}
	}
}

func main() {
	// 创建一个带取消的上下文
	ctx, cancel := context.WithCancel(context.Background())

	// 启动多个工作 goroutine
	for i := 1; i <= 3; i++ {
		go worker(ctx, i)
	}

	// 等待 5 秒后取消上下文
	time.Sleep(5 * time.Second)
	cancel() // 通知所有 goroutine 停止工作

	// 等待一段时间以确保所有 goroutine 都已退出
	time.Sleep(2 * time.Second)
	fmt.Println("All workers stopped.")
}

代码说明

  1. 上下文的创建

    • 使用 context.WithCancel 创建一个可取消的上下文。
  2. 工作 goroutine

    • 每个 worker 在一个无限循环中运行,检查上下文是否已被取消。
  3. 取消上下文

    • 在主函数中,等待 5 秒后调用 cancel(),通知所有 worker 停止工作。
  4. 输出

    • 每个 worker 每秒输出一次工作信息,直到被取消。

注意事项

  • 避免使用全局上下文:尽量避免在函数中使用 context.Background()context.TODO(),而应将上下文信息传递给需要它的函数。
  • 使用 defer:确保调用 cancel(),以避免资源泄露。
  • 优雅地停止 goroutine:通过 ctx.Done() 来监听取消信号,确保 goroutine 能够优雅地停止。

总结

context 包是 Go 语言中用于处理并发操作的重要工具,能够帮助你更好地管理请求的生命周期、取消操作和超时控制。希望这个介绍能帮助您理解 context 包的基本使用。如果您还有其他问题,欢迎提问!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Leetcode 柱状图中最大的矩形

h 是右边界&#xff0c;连续多个高度递增的柱子&#xff0c;如果遇到下一个 h < 栈顶元素(是最大的元素&#xff0c;单调递增栈)&#xff0c;那么会不断出栈来更新计算最大面积。 并非是一次性计算出最大面积的&#xff0c;很重要的一点是while (!stack.isEmpty()这一部分的…

Vivado自定义IP修改顶层后Port and Interface不更新解决方案

问题描述 在整个项目工程中&#xff0c;对自定义IP进行一个比较大的改动&#xff0c;新增了不少端口(这里具体的就是bram的读写端口)&#xff0c;修改是在block design中右击IP编辑在IP编辑工程中进行的。 在修改完所有代码后&#xff08;顶层新增了需要新加的输入输出端口&…

【计算机网络 - 基础问题】每日 3 题(四十九)

✍个人博客&#xff1a;https://blog.csdn.net/Newin2020?typeblog &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/fYaBd &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞…

字节流写入文件

一、创建输出流对象表示的文件三种方式 方法一&#xff1a; FileOutputStream fos new FileOutputStream("fos.txt",true);//最简便方法二&#xff1a; FileOutputStream fos new FileOutputStream(new File("fos.txt"));方法三&#xff1b; File f ne…

HCIP-HarmonyOS Application Developer 习题(十四)

&#xff08;多选&#xff09;1、HarmonyOs为应用提供丰富的Al(Artificial Intelligence)能力&#xff0c;支持开箱即用。下列哪些是它拥有的AI能力? A、通用文字识别 B、词性标注 C、实体识别 D、语音播报 答案&#xff1a;ABCD 分析&#xff1a; AI能力简介二维码生成根据开…

软考高级系统分析师,快背,都是精华知识点!

19、需求变更控制 需求变更控制过程&#xff1a; &#xff08;1&#xff09;变更申请。应记录变更的提出人、日期、申请变更的内容等信息。 &#xff08;2&#xff09;变更评估。对变更的影响范围、严重程度、经济和技术可行性进行系统分析。 &#xff08;3&#xff09;变更…

qt/c++中成员函数返回成员变量并且可以赋值

#创作灵感 最近在做仪表项目&#xff0c;由于客户提供的仪表故障指示灯只有10个固定位置&#xff0c;而故障指示灯却有80多个。为了解决这个问题&#xff0c;进过我的设计&#xff0c;项目中需要返回类的成员变量。并且还可以赋值给它。于是就产生了下面的代码。 class Foo { …

基于Multisim三极管B放大系数放大倍数测量电路设计(含仿真和报告)

【全套资料.zip】三极管B放大系数放大倍数测量电路电路设计Multisim仿真设计数字电子技术 文章目录 功能一、Multisim仿真源文件二、原理文档报告资料下载【Multisim仿真报告讲解视频.zip】 功能 1.用三个数码管显示B的大小&#xff0c;分别显示个位、十位和百位。 2.显示范围…

FineReport 分页

按组分页 按组分页就是让数据按组来进行分页显示&#xff0c;每个组的数据占据一页。 例如报表原本是按照纸张大小进行分页的&#xff0c;现在希望能够按照货主地区进行分页&#xff0c;一个地区的数据显示在同一个页面当中。 在每组数据前设置「行前分页」或者在每组数据后…

健身房管理系统设计与实现(源码+定制+讲解)健身房预约系统开发、健身房会员管理平台、健身房设备管理系统、健身房系统功能优化

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

如实例布局图,如何做到两栏四列,margin间距超出了两段不对齐如何处理

使用 CSS Grid 实现两栏四列布局&#xff1a; <!DOCTYPE html><html lang"en"><head> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width, initial-scale1.0"> <sty…

【原创】java+ssm+mysql在线文件管理系统设计与实现

个人主页&#xff1a;程序猿小小杨 个人简介&#xff1a;从事开发多年&#xff0c;Java、Php、Python、前端开发均有涉猎 博客内容&#xff1a;Java项目实战、项目演示、技术分享 文末有作者名片&#xff0c;希望和大家一起共同进步&#xff0c;你只管努力&#xff0c;剩下的交…

vue中实现css布局

在vue中通过flex布局实现css的s型结构 通过数组截取循环布局&#xff0c;奇数行从左到右&#xff0c;在偶数行从右到左实现s型结构 主要内容分为三部分 中间内容部分 数据格式 items: [{nodeList: [1, 2, 3, 4, 5, 6]},{nodeList: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]}, {nod…

数据结构与算法:贪心与相关力扣题455.分发饼干、376.摆动序列、53.最大子数组和(贪心+动态规划dp)、122.买卖股票的最佳时机Ⅱ

贪心算法 贪心策略在实现时&#xff0c;经常使用到的技巧&#xff1a; 根据某标准建立一个比较器来排序 根据某标准建立一个比较器来组成堆 举例题目&#xff1a;会议室安排 一些项目要占用一个会议室宣讲&#xff0c;会议室不能同时容纳两个项目的宣讲。 给你每一个项目开始…

Go 1.19.4 命令调用、日志、包管理、反射-Day 17

1. 系统命令调用 所谓的命令调用&#xff0c;就是通过os&#xff0c;找到系统中编译好的可执行文件&#xff0c;然后加载到内存中&#xff0c;变成进程。 1.1 exec.LookPath&#xff08;寻找命令&#xff09; 作用&#xff1a; exec.LookPath 函数用于在系统的环境变量中搜索可…

2024年中国工业大模型行业发展研究报告|附43页PDF文件下载

工业大模型伴随着大模型技术的发展&#xff0c;逐渐渗透至工业&#xff0c;处于萌芽阶段。 就大模型的本质而言&#xff0c;是由一系列参数化的数学函数组成的计算系统&#xff0c;且是一个概率模型&#xff0c;其工作机制是基于概率和统计推动进行的&#xff0c;而非真正的理解…

C++ 进阶:类相关特性的深入探讨

⭐在对C 中类的6个默认成员函数有了初步了解之后&#xff0c;现在我们进行对类相关特性的深入探讨&#xff01; &#x1f525;&#x1f525;&#x1f525;【C】类的默认成员函数&#xff1a;深入剖析与应用&#xff08;上&#xff09; 【C】类的默认成员函数&#xff1a;深入剖…

SpringBoot使用RestTemplate实现发送HTTP请求

Java 实现发送 HTTP 请求&#xff0c;系列文章&#xff1a; 《Java使用原生HttpURLConnection实现发送HTTP请求》 《Java使用HttpClient5实现发送HTTP请求》 《SpringBoot使用RestTemplate实现发送HTTP请求》 1、RestTemplate 的介绍 RestTemplate 是 Spring 框架提供的一个用…

【java】抽象类和接口(了解,进阶,到全部掌握)

各位看官早安午安晚安呀 如果您觉得这篇文章对您有帮助的话 欢迎您一键三连&#xff0c;小编尽全力做到更好 欢迎您分享给更多人哦 大家好我们今天来学习Java面向对象的的抽象类和接口&#xff0c;我们大家庭已经来啦~ 一&#xff1a;抽象类 1.1:抽象类概念 在面向对象的概念中…

12寸FAB厂试产到量产实现无纸化要素之软硬件

在12寸先进封装半导体车间从试产到量产的过程中&#xff0c;实现生产过程无纸化&#xff0c;需要综合考虑软硬件的配置。以下是一些关键的规划建议&#xff1a; 1、生产文档管理系统&#xff08;PDM&#xff09;&#xff1a; 采用基于SOLIDWORKS PDM开发的无纸化方案&#xf…