浅谈Go语言并发机制

浅谈Go语言并发机制

Go语言并发简介

并发是指同时执行多个任务的能力。Go语言内置了并发编程的支持,可以非常方便地编写高并发程序。

Go语言的并发模型依赖于go函数和channel这两个基本元素。

Go函数

在Go语言中,我们可以用go关键字来启动一个goroutine(轻量级线程),goroutine的调度由Go语言运行时完成。

以下是一个启动goroutine的简单示例:

package main

import (
    "fmt"
    "time"
)

func main() {
    go func() {
        for i := 1; i <= 5; i++ {
            fmt.Println("goroutine: ", i)
            time.Sleep(time.Second)
        }
    }()
    for i := 1; i <= 5; i++ {
        fmt.Println("main: ", i)
        time.Sleep(time.Second)
    }
}

运行结果如下:

main: 1
goroutine: 1
main: 2
goroutine: 2
main: 3
goroutine: 3
main: 4
goroutine: 4
main: 5
goroutine: 5

这里我们启动了一个匿名函数,使用go关键字创建了一个新的goroutine。在主函数中,我们也打印了5个消息。

可以发现,主函数和goroutine之间并没有严格的交替执行,而是由Go语言运行时随机调度的。

Channel

在Go语言中,我们可以使用channel来进行协程间通信。

channel是Go语言内置的双向链表,支持并发的读写操作。channel类似于Unix中的管道,可以用于协程之间的同步和通信。

以下是一个channel的简单示例:

package main

import (
    "fmt"
)

func main() {
    c := make(chan int)
    go func() {
        for i := 1; i <= 5; i++ {
            c <- i
        }
        close(c)
    }()
    for v := range c {
        fmt.Println(v)
    }
}

运行结果如下:

1
2
3
4
5

这里我们创建了一个大小为1的channel,用于在goroutine和主函数之间通信。在匿名函数中,我们通过c <- i向通道中写入数据,最后通过close(c)关闭通道。在主函数中,我们使用for...range语句从通道中读取数据,并打印输出。

Go语言并发机制

Go语言并发机制通过goroutine和channel来实现,并发模型简单且易于使用,适合处理高并发场景。

以下是一个并发模型的简单示例:

package main

import (
    "fmt"
)

func main() {
    c := make(chan int)
    go func() {
        for i := 1; i <= 5; i++ {
            c <- i
        }
        close(c)
    }()
    for v := range c {
        go func(v int) {
            fmt.Println("goroutine: ", v)
        }(v)
    }
}

运行结果如下:

goroutine:  3
goroutine:  4
goroutine:  5
goroutine:  1
goroutine:  2

这里我们先创建了一个通道,然后在一个goroutine中向通道中写入数据,最后关闭通道。在主函数中,我们使用for...range语句从通道中读取数据,并使用go关键字启动一个新的goroutine打印消息。

从运行结果可以看出,启动的五个goroutine并不是严格按照顺序依次执行,而是由Go语言运行时随机调度的。

另外,由于我们使用了go func(v int){fmt.Println("goroutine: ", v)}(v)的形式来启动goroutine,所以每个goroutine都是在新的线程中执行,相互之间并不会互相影响。而如果不使用go关键字来启动goroutine,则当前的goroutine就会阻塞等待消息被读取,从而无法并发执行其他的任务。

例子说明

goroutine并发下载文件

在多数情况下,我们下载很多小文件的时候,单线程下载速度太慢,很容易使用户等待。使用goroutine则可以简单地达到并发下载的目的。

以下是一个使用goroutine并发下载文件的简单示例:

package main

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

func downloadFile(url string, fileName string) {
    resp, err := http.Get(url)
    defer resp.Body.Close()

    if err != nil {
        fmt.Println("Error: ", err)
        return
    }

    body, err := ioutil.ReadAll(resp.Body)

    if err != nil {
        fmt.Println("Error: ", err)
        return
    }

    err = ioutil.WriteFile(fileName, body, 0644)

    if err != nil {
        fmt.Println("Error: ", err)
        return
    }

    fmt.Println("Downloaded: ", url)
}

func main() {
    c := make(chan int)
    files := []string{
        "https://golang.google.cn/doc/gopher/bumper.png",
        "https://golang.google.cn/pkg/http/",
        "https://golang.google.cn/pkg/io/",
    }
    for _, url := range files {
        go func(url string) {
            fileName := url[strings.LastIndex(url, "/")+1:]
            downloadFile(url, fileName)
            c <- 1
        }(url)
    }
    for i := 0; i < len(files); i++ {
        <-c
    }
}

运行结果如下:

Downloaded:  https://golang.google.cn/pkg/io/
Downloaded:  https://golang.google.cn/doc/gopher/bumper.png
Downloaded:  https://golang.google.cn/pkg/http/

这里我们定义了downloadFile函数用于下载文件,然后在主函数中使用for循环启动多个goroutine并发执行下载任务。我们使用通道来同步各个goroutine执行的任务。

从运行结果中可以看出,这个程序可以同时下载多个文件并且可以很快地完成所有任务。

超时请求

在网络请求中,经常需要设置超时时间,以防止请求长时间未响应而阻塞应用程序。使用goroutine可以方便地实现超时请求的功能。

以下是一个使用goroutine实现超时请求的简单示例:

package main

import (
    "fmt"
    "net/http"
    "time"
)

func main() {
    c := make(chan error)
    url := "https://golang.google.cn/pkg/io/"
    go func() {
        resp, err := http.Get(url)
        if err != nil {
            c <- err
        } else {
            defer resp.Body.Close()
            c <- nil
        }
    }()
    select {
    case err := <-c:
        if err != nil {
            fmt.Println("Error: ", err)
        } else {
            fmt.Println("Request OK")
        }
    case <-time.After(3 * time.Second):
        fmt.Println("Request timed out")
    }
}

运行结果如下:

Request OK

这里我们创建了一个通道和一个goroutine,同时使用了selecttime.After函数来模拟超时。当请求超时或者执行完成后,程序可以自动终止。如果在规定时间内请求没有得到响应,则打印提示信息。

结论

Go语言的并发机制基于goroutine和channel实现,简单易用,适合处理高并发场景。在实际应用中,我们可以通过使用goroutine来实现并发执行任务,使用channel来进行不同goroutine之间的协作和通信,从而实现高效的并发编程。

如果我们在合适的场景下使用goroutine和channel,可以显著地提高程序的性能和吞吐量,从而更好地满足实际需求。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:浅谈Go语言并发机制 - Python技术站

(0)
上一篇 2023年5月17日
下一篇 2023年5月17日

相关文章

  • Java多线程并发编程 Synchronized关键字

    Java多线程并发编程Synchronized关键字 什么是Synchronized关键字? 在Java多线程并发编程中,Synchronized关键字可以用来保证多个线程在访问共享资源时的同步性。它可以实现线程安全的同步操作。 Synchronized关键字的用法 Synchronized关键字可以加在方法和代码块上面。 方法上的Synchronized关…

    多线程 2023年5月16日
    00
  • golang高并发限流操作 ping / telnet

    Golang 高并发限流操作 ping/telnet 的完整攻略 在分布式系统中,高并发请求是不可避免的问题,如何防止恶意攻击和拒绝服务攻击是一个必须解决的问题。Golang 作为一种高性能的编程语言,提供了良好的支持来解决这些问题。本文介绍如何使用 Golang 实现高并发的 ping / telnet 限流操作。 原理简介 在 Golang 中,我们可以…

    多线程 2023年5月16日
    00
  • Java并发编程之Executor接口的使用

    Java并发编程之Executor接口的使用 Java中的线程池在实现多线程编程中有着重要的作用。在Java中,线程池的实现需要通过java.util.concurrent.Executor接口来实现。在本文中,我们将讲解Executor接口的用法,以及如何使用线程池来提高并发效率。 什么是Executor接口? Executor接口是Java线程池的核心接…

    多线程 2023年5月17日
    00
  • SpringCloud LoadBalancerClient 负载均衡原理解析

    SpringCloud LoadBalancerClient 负载均衡原理解析 什么是负载均衡? 负载均衡(Load Balancing)是指将工作请求分担到多个计算资源上进行处理,以达到最优化的资源利用、最大化的吞吐量、最小化响应时间、避免单点故障等目的。 传统的负载均衡方式有硬件负载均衡和软件负载均衡,但这些方式都需要使用专门的设备或者软件,且较为昂贵。…

    多线程 2023年5月17日
    00
  • Java多线程之死锁详解

    Java多线程之死锁详解 什么是死锁 死锁是指两个或多个线程在执行过程中,因争夺资源而造成的一种僵局,若无外力作用,它们无法继续进行下去。 死锁的产生原因 死锁的产生通常由以下四个必要条件引起: 互斥条件: 资源不能被共享,只能被一个线程占用。 请求与保持条件: 线程已经保持了至少一个资源,并且当前正在请求另一个资源。 不剥夺条件: 资源不能强制性地被其他线…

    多线程 2023年5月17日
    00
  • 如何使用Redis锁处理并发问题详解

    下面是使用Redis锁处理并发问题的完整攻略: 什么是Redis锁 Redis锁是应用程序使用的一种机制,用于在高并发环境下保护共享资源。它通常使用Redis作为共享锁存储后端,因为Redis具有高性能和可靠性。Redis锁分为两种类型:基于SETNX命令的简单锁和基于Redlock算法的分布式锁。 简单锁的实现 简单锁的实现方式非常简单,就是使用SETNX…

    多线程 2023年5月16日
    00
  • JAVA多线程并发下的单例模式应用

    接下来我会详细讲解“JAVA多线程并发下的单例模式应用”的完整攻略,包括两个示例说明来帮助理解。 单例模式 单例模式是设计模式中的一种,它保证某个类只有一个实例,并提供一个全局访问点供其他类访问该实例。在多线程并发环境下,单例模式的实现方式需要特别注意线程安全性问题,否则会导致实例化多个对象,违背了单例模式的初衷。 懒汉式单例模式 懒汉式单例模式是指在第一次…

    多线程 2023年5月16日
    00
  • 【java 多线程】守护线程与非守护线程的详解

    Java多线程:守护线程与非守护线程的详解 什么是守护线程? 在Java多线程中,守护线程是一种在后台运行的线程,它不会阻止程序的结束,也不会执行任何没有被其他非守护线程阻止的操作。 换句话说,当程序中最后一个非守护线程结束时,JVM会强制退出来,即使守护线程还在运行。 如何创建守护线程? 可以通过Thread类的setDaemon()方法来创建守护线程,示…

    多线程 2023年5月17日
    00
合作推广
合作推广
分享本页
返回顶部