并发已成为现代软件开发中的关键功能,允许程序同时有效地处理多个任务。 Go也称为Golang,是一种由Google设计的流行编程语言,其核心内置。在本文中,我们将探讨GO中并发的基础知识,包括Goroutines,频道和同步原语。
goroutines
Goroutines是GO并发模型的基石。它们是轻巧的,独立的可执行函数,可以与其他goroutines同时运行。 Goroutines类似于其他编程语言中的线程,但是它们更有效,更易于管理。
要创建一个goroutine,您只需在函数调用之前预先启动go
关键字。例如,以下代码片段创建了一个goroutine,它打印了“你好,世界!”在主要程序继续执行时的背景中:
func main() {
go fmt.Println("Hello, World!")
fmt.Println("This is the main program.")
}
运行此程序时,应该看到输出:
This is the main program.
Hello, World!
go
关键字告诉GO启动新的Goroutine并在后台执行函数fmt.Println("Hello, World!")
。主要程序继续执行,而无需等待Goroutine完成。
您可以在程序中创建尽可能多的goroutines,它们都可以同时运行。 Goroutines的创建和使用很少的内存很便宜,因此您可以在必要时创建数千甚至数百万个。
频道
goroutines非常适合并发,但是他们仍然需要一种彼此交流的方法。频道是GO中的内置数据结构,为Goroutines提供了一种安全有效的方式。
通道就像连接两个goroutines的管道。一个Goroutine可以将值发送到通道,另一个Goroutine可以从通道接收这些值。频道是安全访问的安全性,这意味着多个goroutines可以使用同一渠道而不会引起比赛条件。
要创建一个频道,您可以将make
函数与chan
关键字和类型指定符一起使用。例如,以下代码片段创建了整数的通道:
ch := make(chan int)
您可以使用<-
操作员将值发送到频道中。例如,以下代码段将值42发送到频道ch
:
ch <- 42
您也可以使用<-
操作员从通道接收值。例如,以下代码段从频道ch
接收一个值,并将其存储在变量x
:
中
x := <-ch
如果通道中没有值,则接收操作块,直到有一个值可用。这使Goroutines可以相互同步和交流,而无需显式同步。
缓冲通道
默认情况下,GO中的通道被没有被掩盖,这意味着它们一次只能持有一个值。当Goroutine将值发送到未封闭的通道中时,它会阻止直到另一个Goroutine收到该值。同样,当goroutine从一个未封闭的通道接收值时,它会阻止直到另一个goroutine发送值。
缓冲通道是一次可以容纳多个值的通道。它们允许Goroutines在不阻止的情况下进行异步交流。要创建一个缓冲通道,请在创建频道时指定缓冲区大小。例如,以下代码片段创建了一个缓冲区大小为10:
的整数的缓冲通道
ch := make(chan int, 10)
只要缓冲区不满,您就可以将值发送到缓冲通道中。发送操作块,直到空间可用。同样,只要缓冲区没有空,您就可以从缓冲通道接收值。如果缓冲区为空,则接收操作块,直到将值发送到通道为止。
缓冲通道可通过减少goroutines之间的阻塞和上下文切换来改善并发程序的性能。但是,在使用缓冲通道时,您应该要小心,因为如果没有正确使用,它们可能会导致goroutines僵局。
选择语句
SELECT语句是GO的强大功能,可让您一次处理多个频道操作。它使您可以等待一个或多个频道准备好发送或接收操作,然后执行相应的代码块。
。SELECT语句看起来像一个开关语句,但是它没有测试变量,而是测试多个通道。选择语句中的每种情况都代表通道操作,如果其他情况都没有准备就绪,则执行默认情况。
以下是如何使用Select语句处理两个频道的示例:
ch1 := make(chan int)
ch2 := make(chan int)
go func() {
ch1 <- 42
}()
go func() {
ch2 <- 100
}()
select {
case x := <- ch1:
fmt.Println("Received from ch1:", x)
case x := <- ch2:
fmt.Println("Received from ch2:", x)
}
在此示例中,启动了两个goroutines,将值发送到ch1
和ch2
。 SELECT语句等待ch1
或ch2
准备接收操作,然后执行相应的案例块。在这种情况下,首先接收到ch1
的值,因此执行第一个情况块,并且输出“从CH1:42”接收。
SELECT语句也可以与default
案例一起使用,以处理所有频道都没有准备就绪的情况。例如:
ch1 := make(chan int)
ch2 := make(chan int)
select {
case x := <- ch1:
fmt.Println("Received from ch1:", x)
case x := <- ch2:
fmt.Println("Received from ch2:", x)
default:
fmt.Println("No channels ready.")
}
在此示例中,ch1
和ch2
都没有任何值要接收,因此执行默认情况,并且输出为“未准备好频道”。
同步原语
虽然频道非常适合在goroutines之间进行通信,但有时您需要对程序同步进行更细粒度的控制。 GO提供了几种内置的同步原始分子,包括静音,读写静音和原子操作。
静音
Mutex是一个相互的排除锁,一次只允许一个Goroutine一次访问共享资源。静音者用于保护密码的关键部分,以防止比赛条件,并确保只有一个Goroutine一次修改共享资源。
要在GO中使用Mutex,您首先使用sync.Mutex
类型创建一个新的Mutex。然后,您可以使用Lock
和Unlock
方法分别获取和释放静音。例如:
var mutex sync.Mutex
func someFunction() {
mutex.Lock()
defer mutex.Unlock()
// critical section of code
}
在此示例中,Lock
方法获取了互惠码,而Unlock
方法将其释放。 defer
语句确保即使PANICS的关键部分或早期返回。
读写静音
读写的互联网是一种静音类型,允许多个goroutines同时读取共享资源,但只允许一个goroutine一次写入资源。当您拥有经常阅读但偶尔写的资源时,这很有用。
要在GO中使用read-write Mutex,您可以使用Sync.rwmutex类型创建一个新的Mutex。然后,您可以使用RLock
和RUnlock
方法来获取和释放读取锁定,而Lock
和Unlock
方法分别以获取和释放写锁。例如:
var rwMutex sync.RWMutex
var sharedResource = 42
func readFunction() {
rwMutex.RLock()
defer rwMutex.RUnlock()
// read from sharedResource
}
func writeFunction() {
rwMutex.Lock()
defer rwMutex.Unlock()
// write to sharedResource
}
在此示例中,RLock
方法获取了读取锁,而RUnlock
方法释放了读取锁。 Lock
方法获得了写锁定,而Unlock
方法释放了写锁。多个goroutines可以同时获得读取锁,但是只有一个goroutine可以一次获取写锁。
原子操作
原子操作是在原子上执行的操作,这意味着它们被执行为一个不可分割的步骤。在GO中,原子操作由sync/atomic
软件包提供,用于安全修改共享变量而无需锁或其他同步原始图。
sync/atomic
软件包提供了多个用于执行原子操作的功能,包括AddInt32
,AddInt64
,LoadInt32
,LoadInt64
,LoadInt64
,StoreInt32
和StoreInt64
。例如:
var sharedVariable int64 = 0
func incrementFunction() {
atomic.AddInt64(&sharedVariable, 1)
}
在此示例中,AddInt64
函数从原子上递增sharedVariable
的值,而无需锁定。 &
操作员用于将sharedVariable
的地址传递给函数。
结论
并发是现代软件开发中的关键功能,而GO的内置支持使其成为构建高度并发和可扩展应用程序的绝佳选择。 Goroutines,频道和同步基原始人是强大的工具,可让您编写可以同时有效处理多个任务的高度并发程序。
在本文中,我们探讨了GO中并发的基础知识,包括goroutines,channel和同步原语。我们还讨论了如何使用SELECT语句一次处理多个通道操作,以及如何使用静音词,读写互斥词和原子操作来对同步进行细粒度控制。
虽然GO的并发模型功能强大且易于使用,但编写正确且高效的并发程序仍然具有挑战性。您应该了解并发编程的潜在陷阱,例如种族条件,僵局和生计,并使用最佳实践,例如避免共享可变状态并使用惯用的GO代码来避免这些问题。
> 。总的来说,GO对并发的支持使其成为构建高度并发和可扩展应用程序的绝佳选择,而GO中掌握并发是任何现代软件开发人员的重要技能。
快乐编码!