Go语言入门心法(七): 并发与通道



Go语言入门心法(一): 基础语法

Go语言入门心法(二): 结构体

Go语言入门心法(三): 接口

Go语言入门心法(四): 异常体系

 Go语言入门心法(五): 函数

Go语言入门心法(六): HTTP面向客户端|服务端编程

Go语言入门心法(七): 并发与通道

Go语言入门心法(八): mysql驱动安装报错onnection failed

Go语言入门心法(九): 引入三方依赖

Go语言入门心法(十):Go语言操作MYSQL(CRUD)|事务处理

Go语言入门心法(十一): 文件处理

Go语言入门心法(十二): GORM映射框架

Go语言入门心法(十三): 反射认知升维

 



并行与并发认知升维:


go语言中协程与并发认知

(1) go语言的最大特点,是从语言层面支持并发
(2) Go语言的最大优势在于并发与性能,其性能可以媲美C和C++,并发在网络编程中更是至关重要
(3) 有事光靠硬件的提升无法满足高并发的需求的,因此需从程序的角度来解决高并发问题
(4) 通常并发指在同一段时间内,程序可以执行多个任务;几乎所有的语言都支持并发,而go语言最大的特点就是从语言层面支持并发
(5) 并发编程通常包括: 多线程编程,多进程编程,一家分布式编程
(6) 调用栈: 调用栈是计算机科学中存储有关正在运行的子程序的消息栈,经常被用于存放子程序的返回地址。在调用任何子程序中,主程序都必须暂存子程序运行完毕后应该返回到的地址。
                因此,如果被调用的子程序还要调用其他子程序,其自身的返回地址必须存入调用栈,在其自身运行完毕后再取回。
(7) 并发与并行区别
    7.1: 并行(parallelism):指在同一时刻,有多条指令在多个处理器上同时运行
    7.2: 并发(concurrency):指在同一时刻,只能有一条指令执行,但是多个进程指令被快速第轮换执行,得到宏观上有多个进程同时执行的效果,
                         但是在微观上并不是同时执行,只是把时间片分为了若干段,使得多个进程快速交替执行。
 


一: go语言并发与通道


package main

import (
	"fmt"
	"time"
)

/*
go语言中协程与并发认知

					(1) go语言的最大特点,是从语言层面支持并发
					(2) Go语言的最大优势在于并发与性能,其性能可以媲美C和C++,并发在网络编程中更是至关重要
				    (3) 有时光靠硬件的提升无法满足高并发的需求的,因此需从程序的角度来解决高并发问题
				    (4) 通常并发指在同一段时间内,程序可以执行多个任务;几乎所有的语言都支持并发,而go语言最大的特点就是从语言层面支持并发
				    (5) 并发编程通常包括: 多线程编程,多进程编程,分布式编程
			        (6) 调用栈: 调用栈是计算机科学中存储有关正在运行的子程序的消息栈,经常被用于存放子程序的返回地址。在调用任何子程序中,主程序都必须暂存子程序运行完毕后应该返回到的地址。
		             因此,如果被调用的子程序还要调用其他子程序,其自身的返回地址必须存入调用栈,在其自身运行完毕后再取回。
			        (7) 并发与并行区别
			            7.1: 并行(parallelism):指在同一时刻,有多条指令在多个处理器上同时运行
			            7.2: 并发(concurrency):指在同一时刻,只能有一条指令执行,但是多个进程指令被快速第轮换执行,得到宏观上有多个进程同时执行的效果,
	                         但是在微观上并不是同时执行,只是把时间片分为了若干段,使得多个进程快速交替执行。
*/
func main() {
	println(`
      go语言中协程与并发认知
      (1) go语言的最大特点,是从语言层面支持并发
			(2) Go语言的最大优势在于并发与性能,其性能可以媲美C和C++,并发在网络编程中更是至关重要
			(3) 有时光靠硬件的提升无法满足高并发的需求的,因此需从程序的角度来解决高并发问题
			(4) 通常并发指在同一段时间内,程序可以执行多个任务;几乎所有的语言都支持并发,而go语言最大的特点就是从语言层面支持并发
			(5) 并发编程通常包括: 多线程编程,多进程编程,分布式编程
			(6) 调用栈: 调用栈是计算机科学中存储有关正在运行的子程序的消息栈,经常被用于存放子程序的返回地址。在调用任何子程序中,主程序都必须暂存子程序运行完毕后应该返回到的地址。
		             因此,如果被调用的子程序还要调用其他子程序,其自身的返回地址必须存入调用栈,在其自身运行完毕后再取回。
			(7) 并发与并行区别
			    7.1: 并行(parallelism):指在同一时刻,有多条指令在多个处理器上同时运行
			     7.2: 并发(concurrency):指在同一时刻,只能有一条指令执行,但是多个进程指令被快速第轮换执行,得到宏观上有多个进程同时执行的效果,
	                         但是在微观上并不是同时执行,只是把时间片分为了若干段,使得多个进程快速交替执行。
      在go语言中,编写并发程序时非常简单的,它不需要引入第三方额外的库,创建goroutine,只需要在函数调用语句前面添加go关键字,
      就可以创建并行执行单元。开发人员无须了解任何执行细节,调度器会自动将其安排到合适的线程上去执行;
      在并发编程里,通常要将一个过程分割成几块,然后让不同的goroutine各自负责其中的一块工作;
   `)

	println("该程序有三个模块,分别是main,Task1,Task2,这里使用了两个关键字go将task1和task2改变成了两个并的模块")
	println()
	go goroutineTask1()
	go goroutineTask2()

	for {
		fmt.Println(time.Now().Format("15:04:05"), "正在处理主进程的任务")
		time.Sleep(time.Second * 3)
	}

}

// goroutineTask1 定义任务执行函数
func goroutineTask1() {
	for {
		fmt.Println(time.Now().Format("15:04:05"), "正在执行Task1任务")
		time.Sleep(time.Second * 1) // 休眠1秒
	}
}

// goroutineTask2 定义任务执行函数
func goroutineTask2() {
	for {
		fmt.Println(time.Now().Format("15:04:05"), "正在执行Task2任务")
		time.Sleep(time.Second * 2) // 休眠1秒
	}
}

运行效果:


D:\program_file_worker\go1.20 #gosetup
GOPATH=D:\program_file_worker\go1.20\bin;C:\Users\Administrator\go #gosetup
D:\program_file_worker\go1.20\bin\go.exe build -o C:\Users\Administrator\AppData\Local\Temp\GoLand\___go_build_OOPGoroutineToChannelGrammar_go.exe D:\program_file\go_workspace\org.jd.data\goroutine\OOPGoroutineToChannelGrammar.go #gosetup
C:\Users\Administrator\AppData\Local\Temp\GoLand\___go_build_OOPGoroutineToChannelGrammar_go.exe

      go语言中协程与并发认知
      (1) go语言的最大特点,是从语言层面支持并发
                        (2) Go语言的最大优势在于并发与性能,其性能可以媲美C和C++,并发在网络编程中更是至关重要

                        (3) 有时光靠硬件的提升无法满足高并发的需求的,因此需从程
序的角度来解决高并发问题
                        (4) 通常并发指在同一段时间内,程序可以执行多个任务;几乎所有的语言都支持并发,而go语言最大的特点就是从语言层面支持并发

                        (5) 并发编程通常包括: 多线程编程,多进程编程,分布式编程
的消息栈,经常被用于存放子程序的返回地址。在调用任何子程序中,主程序都必须暂存子程序运行完毕后应该返回到的地址。
的消息栈,经常被用于存放子程序的返回地址。在调用任何子程序中,主程序都必须暂存子程序运行完毕后应该返回到的地址。
                             因此,如果被调用的子程序还要调用其他子程序,其自身的返回地址必须存入调用栈,在其自身运行完毕后再取回。
                        (7) 并发与并行区别
                            7.1: 并行(parallelism):指在同一时刻,有多条指令在多个处理器上同时运行
                             7.2: 并发(concurrency):指在同一时刻,只能有一条指令执行,但是多个进程指令被快速第轮换执行,得到宏观上有多个进程同时执行的效果,
                                 但是在微观上并不是同时执行,只是把时间片分为了若干段,使得多个进程快速交替执行。
      在go语言中,编写并发程序时非常简单的,它不需要引入第三方额外的库,创建goroutine,只需要在函数调用语句前面添加go关键字,
      就可以创建并行执行单元。开发人员无须了解任何执行细节,调度器会自动将其安排到合适的线程上去执行;
      在并发编程里,通常要将一个过程分割成几块,然后让不同的goroutine各自负责其中的一块工作;

该程序有三个模块,分别是main,Task1,Task2,这里使用了两个关键字go将task1和task2改变成了两个并的模块

21:31:14 正在处理主进程的任务
21:31:14 正在执行Task1任务
21:31:14 正在执行Task2任务

21:31:16 正在执行Task1任务
21:31:17 正在执行Task1任务
21:31:17 正在执行Task2任务
21:31:18 正在处理主进程的任务
21:31:18 正在执行Task1任务
21:31:19 正在执行Task2任务
21:31:19 正在执行Task1任务
21:31:20 正在执行Task1任务
21:31:21 正在处理主进程的任务
21:31:21 正在执行Task2任务
21:31:21 正在执行Task1任务

Process finished with the exit code -1073741510 (0xC000013A: interrupted by Ctrl+C)


二: 主|子协程认知

goroutine(协程两种创建方式):

         (1) 关键字 go 被调用的函数名称(参数列表)
         (2) 匿名函数创建协程(goroutine): 关键字go后面可以为匿名函数,格式如下所示:
             go func(参数列表) {
                函数体
             }(调用参数列表)
           (3)当一个程序启动时,其主函数即在一个单独的goroutine中运行,我们称之为main goroutine 

package main

import (
	"fmt"
	"time"
)

/*
goroutine(协程两种创建方式):

		(1) 关键字 go 被调用的函数名称(参数列表)
		(2) 匿名函数创建协程(goroutine): 关键字go后面可以为匿名函数,格式如下所示:
	       go func(参数列表) {
	          函数体
	       }(调用参数列表)
        (3)当一个程序启动时,其主函数即在一个单独的goroutine中运行,我们称之为main goroutine
*/
func main() {
	go Task1()
	println(`
       main函数: 当一个程序启动时,其主函数即在一个单独的goroutine中运行,我们称之为main goroutine,
       新的goroutine会使用go关键字来创建来创建
   `)
}

func Task1() {
	for {
		fmt.Println(time.Now().Format("15:04:05"), "正在处理Task1的任务......")
		time.Sleep(time.Second * 3)
	}
}

运行效果:发现子协程没有执行,这是因为运行go Task1(),程序会立刻返回到main函数,而执行完主协程中println函数后,发现没有执行的代码,程序就会判断执行完毕,终止所有的协程;要让子协程也执行,需要在主协程中添加一些等待逻辑;


=D:\program_file_worker\go1.20 #gosetup
GOPATH=D:\program_file_worker\go1.20\bin;C:\Users\Administrator\go #gosetup
D:\program_file_worker\go1.20\bin\go.exe build -o C:\Users\Administrator\AppData\Local\Temp\GoLand\___go_build_OOPGoroutineToMultiGoroutineGrammar_go.exe D:\program_file\go_workspace\org.jd.data\goroutine\OOPGoroutineToMultiGoroutineGrammar.go #gosetup
C:\Users\Administrator\AppData\Local\Temp\GoLand\___go_build_OOPGoroutineToMultiGoroutineGrammar_go.exe

       main函数: 当一个程序启动时,其主函数即在一个单独的goroutine中运行,我们称之为main goroutine,
       新的goroutine会使用go关键字来创建来创建


Process finished with the exit code 0

添加等待逻辑代码: 

func main() {
   go Task1()
   println(`
       main函数: 当一个程序启动时,其主函数即在一个单独的goroutine中运行,我们称之为main goroutine,
       新的goroutine会使用go关键字来创建来创建
   `)
   time.Sleep(time.Second * 100)
}

 在次运行效果:


GOROOT=D:\program_file_worker\go1.20 #gosetup
GOPATH=D:\program_file_worker\go1.20\bin;C:\Users\Administrator\go #gosetup
D:\program_file_worker\go1.20\bin\go.exe build -o C:\Users\Administrator\AppData\Local\Temp\GoLand\___go_build_OOPGoroutineToMultiGoroutineGrammar_go.exe D:\program_file\go_workspace\org.jd.data\goroutine\OOPGoroutineToMultiGoroutineGrammar.go #gosetup
C:\Users\Administrator\AppData\Local\Temp\GoLand\___go_build_OOPGoroutineToMultiGoroutineGrammar_go.exe

       main函数: 当一个程序启动时,其主函数即在一个单独的goroutine中运行,我们称之为main goroutine,
       新的goroutine会使用go关键字来创建来创建

11:41:27 正在处理Task1的任务......
11:41:30 正在处理Task1的任务......
11:41:33 正在处理Task1的任务......
11:41:36 正在处理Task1的任务......
11:41:39 正在处理Task1的任务......
11:41:42 正在处理Task1的任务......
 

三: 主|子协程协同运行Gosched()

runtime包

   Go语言中runtime(运行时)包实现了一个小型的任务调度器。这个调度器的工作原理和系统对线程调度的原理类似,
   Go语言调度器可以高效地将CPU资源分配给每一个任务;其中包含三个重要的函数
    (1) Gosched()该函数是使当前go协程放弃处理器,以让其他的go协程运行。不会挂起当前go协程,因此当前go协程未来会恢复执行;

package main

import (
	"fmt"
	"runtime"
)

/*
runtime包

	Go语言中runtime(运行时)包实现了一个小型的任务调度器。这个调度器的工作原理和系统对线程调度的原理类似,
	Go语言调度器可以高效地将CPU资源分配给每一个任务;其中包含三个重要的函数
	 (1) Gosched()该函数是使当前go协程放弃处理器,以让其他的go协程运行。不会挂起当前go协程,因此当前go协程未来会恢复执行;
*/
func main() {
	println("匿名的方式创建一个协程任务")
	go func() {
		for i := 0; i < 5; i++ {
			fmt.Println("子协程:", "go start ")
		}
	}()

	for i := 0; i < 2; i++ {
		// 通过协程调度器来让出当前的CUP,让子协程运行后,在运行主协程
		runtime.Gosched()
		fmt.Println("主协程:", "main start ")
	}

}

运行效果: 该效果也可以通过在主协中添加  time.Sleep(time.Second * 3) 逻辑,但是执行顺序不一定能是先子协程,再主携程的顺序;


GOROOT=D:\program_file_worker\go1.20 #gosetup
GOPATH=D:\program_file_worker\go1.20\bin;C:\Users\Administrator\go #gosetup
D:\program_file_worker\go1.20\bin\go.exe build -o C:\Users\Administrator\AppData\Local\Temp\GoLand\___go_build_org_jd_data_org_jd_data_goroutine.exe D:\program_file\go_workspace\org.jd.data\goroutine\OOPGoroutineToMultiGoroutineRuntimeGrammar.go #gosetup
C:\Users\Administrator\AppData\Local\Temp\GoLand\___go_build_org_jd_data_org_jd_data_goroutine.exe
匿名的方式创建一个协程任务
子协程: go start
子协程: go start
子协程: go start
子协程: go start
子协程: go start
主协程: main start
主协程: main start

Process finished with the exit code 0

四: 某协程提前终止操作函数Goexit()

runtime包

		Go语言中runtime(运行时)包实现了一个小型的任务调度器。这个调度器的工作原理和系统对线程调度的原理类似,
		Go语言调度器可以高效地将CPU资源分配给每一个任务;其中包含三个重要的函数
		 (1) Gosched()该函数是使当前go协程放弃处理器,以让其他的go协程运行。不会挂起当前go协程,因此当前go协程未来会恢复执行;
	     (2) Goexit() 该函数功能: 终止调用它自己的go协程,但其他协程不会受到影响;Goexit函数会在终止该go协程前执行所有的defer的函数


package main

import (
	"fmt"
	"runtime"
	"time"
)

/*
	   runtime包

		Go语言中runtime(运行时)包实现了一个小型的任务调度器。这个调度器的工作原理和系统对线程调度的原理类似,
		Go语言调度器可以高效地将CPU资源分配给每一个任务;其中包含三个重要的函数
		 (1) Gosched()该函数是使当前go协程放弃处理器,以让其他的go协程运行。不会挂起当前go协程,因此当前go协程未来会恢复执行;
	     (2) Goexit() 该函数功能: 终止调用它自己的go协程,但其他协程不会受到影响;Goexit函数会在终止该go协程前执行所有的defer的函数
*/
func main() {
	go task1()
	go task2()
	time.Sleep(time.Second * 5)

}

func task1() {
	defer fmt.Println("task1 stop")
	fmt.Println("task1 start")
	fmt.Println("task1 work")
}

func task2() {
	defer fmt.Println("task2 stop")
	fmt.Println("task2 start")
	runtime.Goexit() // 效果与return 一样
	fmt.Println("task2 work")

运行效果:


GOROOT=D:\program_file_worker\go1.20 #gosetup
GOPATH=D:\program_file_worker\go1.20\bin;C:\Users\Administrator\go #gosetup
D:\program_file_worker\go1.20\bin\go.exe build -o C:\Users\Administrator\AppData\Local\Temp\GoLand\___go_build_org_jd_data_org_jd_data_goroutine__1_.exe D:\program_file\go_workspace\org.jd.data\goroutine\OOPGoroutineToMultiGoroutineGoexitGrammar.go #gosetup
C:\Users\Administrator\AppData\Local\Temp\GoLand\___go_build_org_jd_data_org_jd_data_goroutine__1_.exe
task1 start
task1 work
task1 stop
task2 start
task2 stop

Process finished with the exit code 0


从运行结果中可以看出,task2 work并没有输出,在调用了runtime.Goexit()函数后,该协程余下的代码不会被执行,相当于在调用Goexit()函数的位置执行return语句一样 

五: 设置程序运行的CUP数


  runtime包
Go语言中runtime(运行时)包实现了一个小型的任务调度器。这个调度器的工作原理和系统对线程调度的原理类似,
Go语言调度器可以高效地将CPU资源分配给每一个任务;其中包含三个重要的函数
 (1) Gosched()该函数是使当前go协程放弃处理器,以让其他的go协程运行。不会挂起当前go协程,因此当前go协程未来会恢复执行;
 (2) Goexit() 该函数功能: 终止调用它自己的go协程,但其他协程不会受到影响;Goexit函数会在终止该go协程前执行所有的defer的函数
 (3) GOMAXPROCS()函数可以设置程序在运行中所使用的CPU数,在以后的编程中使用得最大的函数之一,Go语言程序默认会使用最大CUP进行计算
          func GOMAXPROCS(n int) int

package main

import (
	"fmt"
	"runtime"
	"time"
)

/*
			   runtime包
				Go语言中runtime(运行时)包实现了一个小型的任务调度器。这个调度器的工作原理和系统对线程调度的原理类似,
				Go语言调度器可以高效地将CPU资源分配给每一个任务;其中包含三个重要的函数
				 (1) Gosched()该函数是使当前go协程放弃处理器,以让其他的go协程运行。不会挂起当前go协程,因此当前go协程未来会恢复执行;
			     (2) Goexit() 该函数功能: 终止调用它自己的go协程,但其他协程不会受到影响;Goexit函数会在终止该go协程前执行所有的defer的函数
		         (3) GOMAXPROCS()函数可以设置程序在运行中所使用的CPU数,在以后的编程中使用得最大的函数之一,Go语言程序默认会使用最大CUP进行计算
	             func GOMAXPROCS(n int) int
*/
func main() {
	n := runtime.GOMAXPROCS(1)

	fmt.Println("先前的CPU核数设置为: ", n)
	last := time.Now()
	for i := 0; i < 100000; i++ {
		go func() {
			// 耗时任务
			a := 999999 ^ 999999
			a = a + 1
		}()
	}
	now := time.Now()
	fmt.Println(now.Sub(last))
}

1个CUP运行效果:


C:\Users\Administrator\AppData\Local\Temp\GoLand\___go_build_org_jd_data_org_jd_data_goroutine__2_.exe
先前的CPU核数设置为:  20
152.5748ms

Process finished with the exit code 0
 


设置4个CUP运行的效果:


先前的CPU核数设置为:  20
40.3461ms

Process finished with the exit code 0

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值