首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Golang之旅22-goroutine

Golang之旅22-goroutine

作者头像
皮大大
发布2021-03-02 15:28:48
发布2021-03-02 15:28:48
7210
举报

进程和线程

进程是程序在操作系统中的一次执行过程,是系统进行资源分配和调度的基本单位。

线程是进程的最小单位,是程序执行的最小单元,是进程的一个执行实例。

  • 一个进程可以创建和销毁多个线程
  • 同一个进程中的多个线程可以并发执行
  • 一个程序至少有一个进程,一个进程至少有一个线程

Go并发编程

go协程

主线程是一个物理线程,直接作用在CPU上,是重量级的,非常耗费资源。 其他编程语言的并发机制是基于线程的,开启过多的线程,耗费资源。 go主线程(有程序员称之为线程,甚至是进程):在一个Go线程上开起多个协程,协程是轻量级的线程,是逻辑态的,对资源的耗费小。

  • 有独立的占空间
  • 共享程序堆空间
  • 调度由用户控制
  • 轻松开启上个协程
  • 协程是轻量级的线程

并发和并行

  • 并发:同一个时间段内执行多个任务(一个人同时和多个人聊天):1---多
  • 并行:同一时刻执行多个任务(多个人同时和一个人聊天)多---1

Go语言并发通过goroutine实现,类似线程,用户轻量级的线程,但不是线程

  • goroutine是Go语言运行时候runtime调度实现,用户自己开的线程,属于轻量级的线程,更加地高效 。线程是操作系统层面实现的
  • Go提供通道channel在多个goroutine进行通信。
  • channelgoroutine是实现Go语言并发模式的基础
MPG模式

Goroutine的调度模型是MPG模式,解释为

  • M:操作系统的主线程,是物理线程;可以在一个CPU或者多个上执行
  • P:协程执行需要的上下文环境
  • G:协程

goroutine实现

  • goroutine的使用是在调用函数的前面加上关键字go
  • 一个goroutine必定对应一个函数
代码语言:javascript
复制
// demo
package main

import (
	"fmt"
	"strconv"
	"time"
)

// 每隔1秒输出hello world
func test()  {
	for i :=1 ; i < 10; i++{
		fmt.Println("test () hello world" + strconv.Itoa(i))
		time.Sleep(time.Second)
	}
}


func main(){
	go test()  // 开启协程

	for i := 1;i < 10; i++{
		fmt.Println("main() hello golang" + strconv.Itoa(i))
		time.Sleep(time.Second)
	}
}
代码语言:javascript
复制
// 开启单个goroutine

package main

import (
	"fmt"
	"sync"
)

// goroutine demo
var wg sync.WaitGroup

func hello(){
	fmt.Println("hello xiaoming")
	wg.Done()  // 通知wg把计数牌减1, 直至到0结束
}

func main(){    // 开启主goroutine去执行main函数

	wg.Add(1)  // 计数牌+1
	go hello()  // 开启一个goroutine去执行hello函数;go关键字
	fmt.Println("hello main")   // 主goroutine做的事情
	// time.Sleep(time.Second)
	wg.Wait()  // 主goroutine结束,小弟可能没有结束;等待上面的所有执行结束
}
代码语言:javascript
复制
// 开启多个goroutine

package main

import (
	"fmt"
	"sync"
)

// goroutine demo
var wg sync.WaitGroup

func hello(i int){
	fmt.Println("hello 小明", i)
	wg.Done()  // 通知wg把计数牌减1
}

func main(){    // 开启主goroutine去执行main函数

	wg.Add(10000)  // 计数牌+1
	for i := 0; i < 10000; i++{
		go hello(i)  // 开启多个goroutine去执行hello函数;go关键字
	}

	fmt.Println("hello main")   // 主goroutine做的事情
	// time.Sleep(time.Second)
	wg.Wait()  // 等待上面的所有执行结束
}
代码语言:javascript
复制
// 匿名函数

package main

import (
	"fmt"
	"sync"
)

var wg sync.WaitGroup
func main(){
    wg.Add(10000)
    for i := 0; i<10000;i++{
        // goroutine 中存在闭包的解决
        go func(i int){  // 传递参数i
            fmt.Println("hello", i)
        	wg.Done()
        }(i)   // i显式地传递匿名函数
    }
    fmt.Println("hello main")
    wg.Wait()    // 阻塞,等待所有的小弟干完活之后才结束
}

GOMAXPROCS

代码语言:javascript
复制
package main

import (
	"fmt"
	"runtime"
)

func main()  {
	// 获取当前成CPU的数量
	num := runtime.NumCPU()
	fmt.Println(num)

	// 自行设置使用多个CPU
	runtime.GOMAXPROCS(num - 1)
	fmt.Println("ok")

}

GOMAXPROCS参数需要使用多少个OS线程来同时执行Go代码,可设置同时执行的最大CPU数,默认是机器上的CPU核心数

  • GO通过runtime.GOMAXPROCS()函数来设置并发执行时占用的CPU逻辑核心数
  • GO1.5之后默认使用全部的CPU逻辑核心数
代码语言:javascript
复制
// 先执行完a,再执行b
package main

import (
	"fmt"
	"runtime"
	"sync"
)

var wg sync.WaitGroup

func a(){
	for i := 1; i<10; i++{
		fmt.Println("A", i)
	}
	wg.Done()
}

func b(){
	for i := 1; i<10; i++{
		fmt.Println("B", i)
	}
	wg.Done()
}

func main(){
	runtime.GOMAXPROCS(1)  // 只占用一个CPU核心
	wg.Add(2)  // 开启两个goroutine:a b
	go a()
	go b()
	wg.Wait()
	//time.Sleep(time.Second)
    
    // ab同时执行,混合在一起
    runtime.GOMAXPROCS(4)  // 只占用4个CPU核心
	wg.Add(2)  // 开启两个goroutine:a b
	go a()
	go b()
	wg.Wait()
}

操作系统线程和goroutine关系

  • 一个操作系统线程对应用户多个的goroutine
  • go程序可以同时使用多个操作系统线程
  • goroutineOS线程是多对多的关系,即m:n:m个goroutine分配到n个操作系统线程中,比如1000个goroutine分配到8个系统线程中
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-10-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 进程和线程
  • Go并发编程
    • go协程
    • MPG模式
  • goroutine实现
  • GOMAXPROCS
  • 操作系统线程和goroutine关系
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档