go语言之路

go高并发之协程的概念以及入门案例

go高并发之协程的概念以及入门案例

近期将更新有关go高并发的一些知识

将会有三个系列

  • go协程(goroutine)
  • go管道(channel)
  • goroutine + channel 构建高并发程序

这三系列将解决如下问题?

要求统2-90000000000的数字中,那些是素数?
//显然用传统的方法是不可行的
//这时候用到goroutine + go channel 可以有效的解决问题

本文是有关go协程的一些概念以及入门案例

本博客将新增纯享版文章点此查看

效果如下图

什么是进程与线程?

进程是一个运行在自己内存地址空间里的独立执行体

一个应用程序就是一个进程

在linux中使用top命令即可看到各种进程

可以看到第一个进程就是netease,网易云音乐,有自己独立的一个进程id

一个进程由一个或多个线程组合

这些线程是共享同一个内存地址的一起工作的执行体

比如

当下载网盘中的数据时,可以同时下载几个文件

这就是多线程,以便加快速度

一个进程中包含一个或多个线程

并发与并行的区别

这里涉及到一个问题,很多同学搞不清楚并发与并行的区别,这里我根据我根据知乎上这个问题某位网友的例子,我觉得很好

  • 你吃饭吃到一半,电话来了,你一直到吃完了以后才去接,这就说明你不支持并发也不支持并行
  • 你吃饭吃到一半,电话来了,你停了下来接了电话,接完后继续吃饭,这说明你支持并发
  • 你吃饭吃到一半,电话来了,你一边打电话一边吃饭,这说明你支持并行

并发:你有处理多个任务的能力,不一定同时(一个CPU轮流)

并行:有同时处理多个任务的能力(多个CPU同时)

并发和并行都可以是很多个线程,就看这些线程能不能同时被(多个)CPU执行,可以说明是并行,并发是多个线程被一个CPU轮流切换着执行

线程是一个物理线程,直接作用在cpu上的,是重量级的,非常耗费cpu资源。其他编程语言实现并发是通过开启多个线程,这样对资源耗费太大。

go实现并发和并行的方式

在go中,应用程序并发处理的部分被称作goroutines(协程)

它是轻量级的线程

占用的的内存和资源小

go协程的特点

  • 有独立的栈空间
  • 共享程序堆空间
  • 调度由用户控制
  • 协程是轻量级的线程

go实现并行

可以指定程序使用的cpu数

实现多核运算,带来性能的提升

这也是go最大的亮点

快速入门案例

首先来一个常规的程序

package main
import "fmt"
func TestHello()  {
   for i := 0; i < 5; i++ {
      fmt.Println("testHello()", "HelloWorld")
       //等待一秒
      time.Sleep(time.Second)
   }
   fmt.Println("test() complete!")
}
func main()  {
   TestHello()
   for i := 0; i < 5; i++ {
      fmt.Println("main()", "HelloWorld")
       //等待一秒
      time.Sleep(time.Second)
   }
   fmt.Println("main() complete!")
}

程序执行的结果

显然是先完成TestHello()的调用

再去输出下面的内容

再看goroutine的案例

仅仅在调用的TestHello前面加了go关键字

即开启一个协程

package main
import (
   "fmt"
   "time"
)
/*
   //快速入门
   在主线程(可以理解成进程)中,开启一个goroutine,该协程每隔1秒输出"hello world"
   在主线程中也每隔一秒中输出"hello world",输出10次后,退出程序
   要求主线程和goroutine同时执行
 */
func TestHello()  {
   for i := 0; i < 5; i++ {
      fmt.Println("testHello()", "HelloWorld")
      //等待一秒
      time.Sleep(time.Second)
   }
   fmt.Println("test() complete!")
}
func main()  {
   //调用TestHello()
   go TestHello()
   for i := 0; i < 5; i++ {
      fmt.Println("main()", "HelloWorld")
      //等待一秒
      time.Sleep(time.Second)
   }
   fmt.Println("main() complete!")
}

常规方式的流程图

使用goroutine流程图

再来一个实际的例子

比如现在我要下载两个文件

使用goroutine并发执行

package main

import (
   "fmt"
   "time"
)

/*
   //快速入门
   在主线程(可以理解成进程)中,开启一个goroutine,该协程每隔1秒输出"hello world"
   在主线程中也每隔一秒中输出"hello world",输出10次后,退出程序
   要求主线程和goroutine同时执行
 */

func Download1()  {
   fmt.Println("开始下载1.avi")
   //假设需要5秒的时间
   time.Sleep(5 * time.Second)
   fmt.Println("complete 1.avi")
}

func Download2()  {
   fmt.Println("开始下载2.avi")
   //假设需要4秒的时间
   time.Sleep(4 * time.Second)
   fmt.Println("complete 2.avi")
}

func main()  {
   start := time.Now()
   fmt.Println("开始执行下载任务")
   go Download1()
   go Download2()
   //给协程6秒钟的时间 防止打印也需要时间造成任务没有完成
   time.Sleep(6 * time.Second)
   fmt.Println("complete all")
   end := time.Now()
   delta := end.Sub(start)
   fmt.Println("函数执行时间", delta)

}

试想若有几百个甚至几万个这样的任务

我们开启几万个协程就可以完美解决问题

对于go来说做到开启几万个协程很轻松

而其他的编程语言会占用过多的资源

而且go的语法更为简单

从细节浅谈为什么程序员为什么要学习linux

上一篇

ubuntu系统下如何使用systemctl管理系统服务

下一篇

你也可能喜欢

发表评论

您的电子邮件地址不会被公开。 必填项已用 * 标注

提示:点击验证后方可评论!

插入图片

微信扫一扫

微信扫一扫