首页 > 编程语言 >Go语言并发编程:轻松驾驭多线程世界(九)

Go语言并发编程:轻松驾驭多线程世界(九)

时间:2024-11-12 17:47:25浏览次数:3  
标签:wg main 编程 Goroutine sync 并发 Go 多线程

Go语言并发编程:轻松驾驭多线程世界在这里插入图片描述

在这里插入图片描述
在现代编程中,并发 是让你的程序变得更强大、更高效的关键技能。幸运的是,Go语言提供了一种简单、直观的方式来处理并发任务,使用轻量级的 GoroutineChannel,让我们能够像指挥交通一样简单地处理多个任务。今天,我们将一起深入探讨Go语言的并发编程模型,轻松搞定并发编程!

1. Goroutine:Go语言的轻量级线程

什么是 Goroutine?

Goroutine 是 Go 中的轻量级线程,启动一个 Goroutine 比操作系统的线程开销小得多。你只需简单地在函数调用前加上一个 go 关键字,Go 就会帮你把这个函数放到独立的 Goroutine 中执行。Go程序可以同时跑成千上万的Goroutine,而不会像传统线程那样消耗大量资源。

启动 Goroutine

启动一个 Goroutine 非常简单,只需要在函数调用前加上 go 关键字:

package main

import (
	"fmt"
	"time"
)

func sayHello() {
	fmt.Println("Hello from Goroutine!")
}

func main() {
	go sayHello() // 启动一个 Goroutine
	time.Sleep(1 * time.Second) // 给 Goroutine 一些时间执行
	fmt.Println("Hello from main!")
}

在这个示例中,sayHello 函数在一个单独的 Goroutine 中运行,而主程序继续往下执行。在实际开发中,这种非阻塞的行为非常适合处理并发任务,比如处理多个请求或后台任务。

Goroutine 的生命周期

  • 启动:使用 go 启动一个 Goroutine,代码会立即并发执行。
  • 运行中:Goroutine 的运行是并发的,可能会与主程序或其他Goroutine同时执行。
  • 结束:当 Goroutine 的任务完成,或遇到 returnpanic 时,Goroutine 会自行结束。

一个很重要的点是,主程序(main Goroutine)退出时,所有其他 Goroutine 都会立刻停止。所以,你需要确保主 Goroutine 给予足够的时间让其他 Goroutine 完成任务。

2. 通道(Channel)

Goroutine 之间需要通过某种方式通信,Go为此设计了 通道(Channel),让你可以安全、简单地在线程间传递数据。通道就像是Goroutine之间的邮递员:一个Goroutine把数据通过通道送出去,另一个Goroutine可以通过通道把数据接收过来。

Channel 的基本用法:无缓冲和有缓冲

无缓冲通道

无缓冲通道的特点是:发送和接收必须同时发生,否则会导致发送方或接收方阻塞。

package main

import (
	"fmt"
)

func main() {
	ch := make(chan string)

	go func() {
		ch <- "Hello from Goroutine!" // 发送数据
	}()

	msg := <-ch // 接收数据
	fmt.Println(msg)
}

在这里,ch 是一个无缓冲的通道,Goroutine 通过 ch <- 向通道发送数据,主程序通过 <-ch 接收数据。

有缓冲通道

有缓冲通道允许你在通道中放置一定数量的值,而不需要立即被接收。

package main

import (
	"fmt"
)

func main() {
	ch := make(chan string, 2) // 创建一个带2个缓冲的通道

	ch <- "Message 1"
	ch <- "Message 2"

	fmt.Println(<-ch) // 输出: Message 1
	fmt.Println(<-ch) // 输出: Message 2
}

有缓冲通道就像一个“快递仓库”,可以暂时存储数据,等到合适的时候再接收。

Channel 的发送与接收

  • 发送数据:使用 <- 向通道发送数据:ch <- value
  • 接收数据:使用 <- 从通道接收数据:value := <-ch

select 语句

在Go中,select 语句允许你同时等待多个通道操作,是处理并发任务的利器。它有点像 switch,但专门用来处理通道的发送和接收。

package main

import (
	"fmt"
	"time"
)

func main() {
	ch1 := make(chan string)
	ch2 := make(chan string)

	go func() {
		time.Sleep(1 * time.Second)
		ch1 <- "First message"
	}()

	go func() {
		time.Sleep(2 * time.Second)
		ch2 <- "Second message"
	}()

	select {
	case msg1 := <-ch1:
		fmt.Println("Received:", msg1)
	case msg2 := <-ch2:
		fmt.Println("Received:", msg2)
	}
}

在这个例子中,select 会等待第一个收到数据的通道,并执行对应的代码块。它让你能够轻松地处理多个并发任务,而无需担心数据的竞争条件。

3. 并发模型:Go语言的CSP模型

Go语言的并发编程基于 CSP模型(Communicating Sequential Processes),即“通过通信实现并发”。Go通过 Goroutine 和 Channel 实现了这个模型,让多个 Goroutine 可以安全、清晰地通信和协作。

CSP模型的核心思想是:不要通过共享内存来通信,而是通过通信来共享内存。在Go中,Channel扮演了这种通信媒介的角色,简化了并发编程的复杂性。

4. 并发安全与 sync

在并发编程中,多个 Goroutine 同时读写同一个资源(比如变量)时,可能会出现数据竞态问题。Go提供了 sync 包中的一些工具来确保并发操作的安全。

sync.WaitGroup

sync.WaitGroup 允许主 Goroutine 等待其他 Goroutine 完成工作,非常适合协调多个并发任务。

package main

import (
	"fmt"
	"sync"
)

func worker(id int, wg *sync.WaitGroup) {
	defer wg.Done() // 完成任务后通知 WaitGroup
	fmt.Printf("Worker %d starting\n", id)
}

func main() {
	var wg sync.WaitGroup

	for i := 1; i <= 5; i++ {
		wg.Add(1) // 向 WaitGroup 注册一个 Goroutine
		go worker(i, &wg)
	}

	wg.Wait() // 等待所有注册的 Goroutine 完成
	fmt.Println("All workers done")
}

互斥锁(sync.Mutex)

sync.Mutex 是一种互斥锁,确保一次只有一个 Goroutine 能够访问某个共享资源。

package main

import (
	"fmt"
	"sync"
)

type Counter struct {
	mu    sync.Mutex
	value int
}

func (c *Counter) increment() {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.value++
}

func main() {
	var wg sync.WaitGroup
	counter := Counter{}

	for i := 0; i < 5; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			for j := 0; j < 1000; j++ {
				counter.increment()
			}
		}()
	}

	wg.Wait()
	fmt.Println("Final counter value:", counter.value)
}

sync.Mutex 确保每次只有一个 Goroutine 能够执行 increment 函数,避免了竞态条件。

原子操作(sync/atomic)

sync/atomic 提供了更底层的原子操作来处理并发问题,比如增加或减少一个整数而不需要加锁。

package main

import (
	"fmt"
	"sync"
	"sync/atomic"
)

func main() {
	var counter int64
	var wg sync.WaitGroup

	for i := 0; i < 5; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			for j := 0; j < 1000; j++ {
				atomic.AddInt64(&counter, 1)
			}
		}()
	}

	wg.Wait()
	fmt.Println("Final counter value:", counter)
}

使用 atomic 可以避免锁的开销,并确保并发安全。

结论

Go语言的并发编程模型让处理并发任务变得既简单又高效。通过 Goroutine 和 Channel,你可以轻松地启动并管理多个任务,并使用 sync 包中的工具确保数据的并发安全。在实际开发中,Go的并发机制尤其适用于高并发服务、后台任务处理等场景。

希望通过这篇博客,你对Go语言的并发编程有了更深入的理解。记住,Goroutine 和 Channel 是你在Go世界中穿梭的秘密武器,让你的程序在多任务处理上如虎添翼!

标签:wg,main,编程,Goroutine,sync,并发,Go,多线程
From: https://blog.csdn.net/weixin_43215013/article/details/143690793

相关文章

  • Go 语言已立足主流,编程语言排行榜24 年 11 月
    Go语言概述Go语言,简称Golang,是由Google的RobertGriesemer、RobPike和KenThompson在2007年设计,并于2009年11月正式宣布推出的静态类型、编译型开源编程语言。Go语言以其提高编程效率、软件构建速度和运行时性能的设计目标,以及简洁的语法、快速的编译速度和出色的并发处理能......
  • 基于Python实现的django农业垃圾分类管理系统的设计与实现
    《[含文档+PPT+源码等]精品基于Python实现的django农业垃圾分类管理系统的设计与实现》该项目含有源码、文档、PPT、配套开发软件、软件安装教程、项目发布教程、包运行成功以及课程答疑与微信售后交流群、送查重系统不限次数免费查重等福利!软件开发环境及开发工具:开发语言:py......
  • ResumeSDK简历解析库编程案例
    目录1、软件概述2、编程案例2.1、官网案例(阿里云)2.2、优化案例3、解析结果1、软件概述ResumeSDK简历解析是北京无奇科技有限公司研发,业界领先的智能简历解析和人岗匹配算法厂商,提供专业的AI招聘技术服务,致力于人力资源行业智能化这一进程。并已经上线阿里云或腾讯云,......
  • Django数据库操作
    1.ORM框架Django提供的ORM框架,可以简化MySQL代码,节省书写MySQL语句的时间。具体功能如下:创建、修改、删除数据库的表(不用写SQL语句,无法创建数据库)。修改表中数据(不用写SQL语句)。2.ORM使用安装mysql包注:Windows系统大概率安装失败,可直接去https://pypi.org/proje......
  • 如何在60分钟内进行ASO竞争对手分析(App Store 和 Google Play Store)
       如今,移动应用程序的数量已达数以百万计,无论您是否是市场新手,都不能否认您迟早会面临激烈的竞争。因此,ASO竞争对手分析是您取得成功的重要方面。如果您不知道自己的竞争对手是谁,您就不可能达到顶峰或保持领先地位。在这篇文章中,让我们介绍一下在不到60分钟的时间内对竞......
  • mongos 分片集群
    1、先配置配置服务器,113-114两个集群,端口27018,配置文件/etc/mongod27018.conf,服务mongod27018.servicemongo--host127.0.0.1--port27018useadmindb.auth('root','password')rs.stauts()2、配置分片服务器113-1186个分片,每个分片又是一个单独的集群模式,端口27017......
  • 深入 Java 多线程:解锁并发编程的奥秘
    在当今的软件开发世界中,性能和高并发是衡量一个应用程序成败的关键因素。无论是处理高流量的网络请求、执行复杂的数据分析任务,还是管理后台服务中的资源,Java多线程编程都是开发者必备的技能之一。本文将带领你深入Java多线程的世界,解锁并发编程的奥秘。1.并发与并行的区......
  • 《TCP/IP网络编程》学习笔记 | Chapter 11:进程间通信
    《TCP/IP网络编程》学习笔记|Chapter11:进程间通信《TCP/IP网络编程》学习笔记|Chapter11:进程间通信进程间通信的基本概念通过管道实现进程间通信通过管道进行进程间双向通信运用进程间通信习题(1)什么是进程间通信?分别从概念和内存的角度进行说明。(2)进程间通信需要......
  • 自己设计的LOGO怎么申请版权?
    自己设计的LOGO申请版权的流程大致如下:确定版权归属:在开始申请版权之前,确保你知道LOGO的创作者。通常,LOGO的创作者是该版权的所有者,有权申请版权保护。如果LOGO是由多人共同创作,则共同成为创作者,共同拥有该版权。进行版权登记:要进行版权登记,你需要向国家版权局或当地版权局......
  • go websocket 服务 及 应用 nginx 的配置
    gowebsocket开启服务ws,wss服务5001对应的是ws,5000对应的是wssgofunc(){ iferr:=r.Run(fmt.Sprintf(":%d",c.WsPort));err!=nil{ global.Logger.Panic("启动失败ws:",err) }}()iferr:=r.RunTLS(fmt.Sprintf(":%d",c.WssPort......