ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Golang并发编程入门教程

2021-12-26 10:00:47  阅读:140  来源: 互联网

标签:main 编程 fmt 入门教程 Golang Println func go runtime


时间单位

  • 1S = 1000ms
  • 1ms = 1000us
  • 1us = 1000ns

并发与并行

  • 并行: 借助多核 cpu 实现。 (真 并行)
  • 并发:
    • 宏观:用户体验上,程序在并行执行。
    • 微观:多个计划任务,顺序执行。在飞快的切换。轮换使用 cpu 时间轮片。 【假 并行】

进程并发

  • 程序:编译成功得到的二进制文件。
    • 占用磁盘空间
    • 死的
    • 系统中相同的程序一般只有一个,有可能存在不同版本号的程序
  • 进程:运行起来程序。 占用系统资源。
    • 进程是根据程序创建的,运行在内存中
    • 活的
    • 一个程序可以启动N个进程
  • 一个程序可以启动N个进程,程序和进程的关系是1对N的关系。

进程状态

  • 初始态
  • 就绪态:程序会经常从就绪态切换到运行态。
  • 运行态:只有运行态能切换到阻塞态。
  • 挂起(阻塞)态:阻塞态可以切换回运行态继续往下执行。
  • 终止(停止)态

线程并发

  • 线程:LWP 轻量级的 进程。最小的执行单位。是cpu分配时间轮片的对象。
  • 进程: 最小的系统资源分配单位。

同步

  • 协同步调。规划先后顺序。
  • 线程同步机制:
    • 互斥锁(互斥量):建议锁。拿到锁以后,才能访问数据,没有拿到锁的线程,阻塞等待。等到拿锁的线程释放锁。
    • 读写锁:一把锁(读属性、写属性)。 写独占,读共享。 写锁优先级高。

协程并发

  • Python、Lua、Rust。。。
  • 21世纪
  • 线程会io阻塞,协程可以在线程io阻塞期间相互切换,执行其他不需要消耗io操作的协程
  • 提高程序执行的效率

总结

  • 进程、线程、协程 都可以完成并发。
  • 稳定性强、节省资源、效率高。
  • 老板——手机:
    • 生产线 —— 设备、材料、厂房 —— 进程。(资源分配单位)
    • 工人 —— 线程。 —— 单进程、单线程的 程序。
    • 50 工人 —— 50 线程。 ——单进程、多线程的 程序。
    • 10 条生产线 —— 500 工人 —— 。多进程、多线程的 程序。
    • 利用闲暇时间义务搬砖 —— 协程。—— 多进程、多线程、多协程 程序。

创建Goroutine程

  • 创建于进程中。 直接使用 go 关键,放置于 函数调用前面,产生一个 go程。 并发。

示例代码:

package main

import (
	"fmt"
	`time`
)

// 唱歌
func sing()  {
	for i:=0;i<50;i++ {
		fmt.Println("----正在唱:隔壁泰山----")
	}
}

// 跳舞
func dance()  {
	for i:=0;i<50;i++ {
		fmt.Println("----正在跳舞:赵四街舞----")
	}
}

func main()  {
	go sing() // 开启goroutine
	go dance() // 另一个goroutine,会抢夺CPU执行权,两个goroutine交替执行
	
	time.Sleep(time.Second)
}

Goroutine的特性:【重点】

  • 主go程结束,子go程随之退出。

示例代码:

package main

import (
	"fmt"
	"time"
)

func main() {
	
	go func() { // 创建一个 子go 程
		for i := 0; i < 5; i++ {
			fmt.Println("------I'm goroutine -------")
			time.Sleep(time.Second)
		}
	}()
	
	// 主goroutine结束,子goroutine随着退出
	// 所以,子goroutine很有可能没有执行完就退出了
	fmt.Println("------I'm main-------")
}

runtime.Gosched()

  • 出让当前go程所占用的 cpu时间片。当再次获得cpu时,从出让位置继续回复执行。
  • 时间片轮转调度算法。

示例代码:

package main

import (
	"fmt"
	"runtime"
)

func main() {
	
	go func() {
		for {
			runtime.Gosched()
			fmt.Println("goroutine 1执行。。。。")
		}
	}()
	
	for {
		runtime.Gosched() // 出让当前 cpu 时间片。
		fmt.Println("主go程执行。。。。")
	}
}

runtime.Goexit()

  • return: 返回当前函数调用到调用者那里去。 return之前的 defer 注册生效。
  • Goexit(): 结束调用该函数的当前go程。Goexit():之前注册的 defer都生效。

示例代码:

package main

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

func test() {
	defer fmt.Println("退出子go程之前")
	// return
	runtime.Goexit() // 退出当前go程。
	defer fmt.Println("退出子go程之后")
}

func main() {
	fmt.Println("主go程开始执行")
	
	go func() {
		fmt.Println("父go程执行之前")
		
		// runtime.Goexit() 会确保defer执行
		defer fmt.Println("defer 父go程执行之后")
		go test()
		
		fmt.Println("父go程执行之后")
	}()
	
	fmt.Println("主go程结束执行")
	time.Sleep(time.Second)
}

runtime.GOMAXPROCS()

  • 设置当前 进程使用的最大cpu核数。 返回 上一次调用成功的设置值。 首次调用返回默认值。

示例代码:

package main

import (
	"fmt"
	"runtime"
)

func main() {
	
	fmt.Println(runtime.GOROOT())
	
	n := runtime.GOMAXPROCS(0) // 0表示最大
	fmt.Println("n = ", n)     // 返回上次设置成功的设置值
	
	n = runtime.GOMAXPROCS(2)
	fmt.Println("n = ", n) // 16
	
	n = runtime.GOMAXPROCS(4)
	fmt.Println("n = ", n) // 2
	
	n = runtime.GOMAXPROCS(8)
	fmt.Println("n = ", n) // 4
	
	n = runtime.GOMAXPROCS(1)
	fmt.Println("n = ", n) // 8
}

补充知识点

  • 每当有一个进程启动时,系统会自动打开三个文件: 标准输入、标准输出、标准错误。 —— 对应三个文件: stdin、stdout、stderr
  • 当进行运行结束。操作系统自动关闭三个文件。

channel

  • 是一种数据类型。 对应一个“管道”(通道 FIFO)
  • channel的定义:
    • make (chan 在channel中传递的数据类型, 容量)
      • 容量= 0: 无缓冲channel
      • 容量 > 0 :有缓冲channel
    • make(chan int) 或 make (chan string , 0)
  • channel有两个端:
    • 一端:写端(传入端) chan <-
    • 另一端: 读端(传出端)<- chan
    • 要求:读端和写端必须同时满足条件,才在shan上进行数据流动。否则,则阻塞。
  • channel是一种先进先出(FIFO)的队列类型

示例代码

package main

import (
	"fmt"
	"time"
)

// 全局定义channel, 用来完成数据同步
var flagChan = make(chan struct{}) // 空struct不占用空间

// 定义一台打印机
func printer(s string) {
	for _, ch := range s {
		fmt.Printf("%c", ch) // 屏幕:stdout
		time.Sleep(300 * time.Millisecond)
	}
}

// 定义两个人使用打印机
func person1() { // person 先执行。
	printer("hello")
	flagChan <- struct{}{} // 写入数据,解除阻塞
}
func person2() { // person 后执行
	printer("world")
	flagChan <- struct{}{} // 写入数据,解除阻塞
}

func main() {
	go person1()
	<-flagChan // 拿到数据之前,一直阻塞,所以会等到go程执行结束,直到写入
	
	go person2()
	<- flagChan
	
	fmt.Println("\n程序执行结束!!!")
}

标签:main,编程,fmt,入门教程,Golang,Println,func,go,runtime
来源: https://blog.csdn.net/qq_37703224/article/details/122152041

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有