学习《GO语言学习笔记》的学习笔记之8.1 并发之定义 (详解)

本文适合初学者阅读
  • 只须在函数调用前添加go 关键字即可创建并发任务.
  • 关键字go并非执行并发操作, 而是创建一个并发任务单元.
  • 与defer一样, goroutine也会因"延迟执行"而立即计算并复制执行参数.
package main

import (
	"log"
	"time"
)

var c int

func counter() int {
	c++
	return c
}

func main() {
	a := 100

	go func(x, y int) {
		time.Sleep(time.Second)       // 让goroutine在main逻辑之后执行
		log.Println("go:", x, y)
	}(a, counter())                   // 立即计算并复制参数, 此时a=100,  counter() = 1


	a += 100
	log.Println("main:", a, counter())   // 正常执行, 此时 a=200, counter() = 2

	time.Sleep(time.Second * 3)           // 等待goroutine结束
}


// resultout:
// 2019/03/12 16:24:58 main: 200 2
// 2019/03/12 16:24:59 go: 100 1
wait
  • 进程退出时,不会等待并发任务结束, 可用通道(channel)阻塞, 然后发出退出信号.
package main

import (
	"log"
	"time"
)

func main() {
	exit := make(chan struct{}) // 创建通道. 因为仅是通知, 没有实际意义

	go func() {
		time.Sleep(time.Second)
		log.Println("goroutine done.")

		close(exit) // 关闭通道, 发出信号
	}()

	log.Println("main ...")
	<-exit // 如通道关闭, 立即解除阻塞
	log.Println("main exit.")
}

// resultout:
// 2019/03/12 16:36:29 main ...
// 2019/03/12 16:36:30 goroutine done.
// 2019/03/12 16:36:30 main exit.
  • 若要等待多个任务结束, 推荐使用sync.WaitGroup, 通过设定计数器, 让每个goroutine在退出前递减, 直至归零时解除阻塞.
package main

import (
	"log"
	"sync"
	"time"
)

func main() {
	var wg sync.WaitGroup

	for i := 0; i < 10; i++ {
		wg.Add(1) // 累加计数

		go func(id int) {
			defer wg.Done() // 递减计数

			time.Sleep(time.Second)
			log.Println("goroutine", id, "done.")
		}(i)

	}

	log.Println("main...")
	wg.Wait() // 阻塞; 直到计数归零
	log.Println("main exit.")
}


// resultout
// 2019/03/12 17:01:52 main...
// 2019/03/12 17:01:53 goroutine 0 done.
// 2019/03/12 17:01:53 goroutine 1 done.
// 2019/03/12 17:01:53 goroutine 5 done.
// 2019/03/12 17:01:53 goroutine 3 done.
// 2019/03/12 17:01:53 goroutine 9 done.
// 2019/03/12 17:01:53 goroutine 8 done.
// 2019/03/12 17:01:53 goroutine 4 done.
// 2019/03/12 17:01:53 goroutine 6 done.
// 2019/03/12 17:01:53 goroutine 7 done.
// 2019/03/12 17:01:53 goroutine 2 done.
// 2019/03/12 17:01:53 main exit.

  • 尽管WaitGroup.Add 实现了原子操作, 但建议在goroutine外累加计数器, 以免Add尚未执行, wait已经退出
package main

import (
	"log"
	"sync"
	"time"
)

func main() {
	var wg sync.WaitGroup

	wg.Add(1)

	go func() {
		wg.Wait() // 等待归零,  解除阻塞
		log.Println("wait exit.")
	}()

	go func() {
		time.Sleep(time.Second)
		log.Println("done.")
		wg.Done() // 递减计数
	}()

	wg.Wait() // 等待归零,  解除阻塞
	log.Println("main exit.")
}

// resultout
// 2019/03/12 17:21:05 done.
// 2019/03/12 17:21:06 wait exit.
// 2019/03/12 17:21:06 main exit.
  • GOMAXPROCS
  • 运行时可能会创建很多线程, 但任何时候仅有限的几个线程参与并发任务执行. 该数量与处理器核数相等, 可用runtine.GOMAXPROCS函数(或环境变量)修改.
package main

import (
	"log"
	"math"
	"runtime"
	"sync"
)

func count() {
	x := 0

	for i := 0; i < math.MaxUint32; i++ {
		x += i
	}
	log.Println(x)
}

// 循环测试
func test(n int) {
	for i := 0; i < n; i++ {
		count()
	}
}

// 并发测试
func test2(n int) {
	var wg sync.WaitGroup

	wg.Add(n)

	for i := 0; i < n; i++ {
		go func() {
			count()
			wg.Done()
		}()
	}

	wg.Wait()

}

func main() {
	n := runtime.GOMAXPROCS(0)
	test(n)  // 两个交替执行
	// test2(n)
}


// test(n) resultout
// 2019/03/12 17:36:01 9223372030412324865
// 2019/03/12 17:36:15 9223372030412324865
// 2019/03/12 17:36:28 9223372030412324865
// 2019/03/12 17:36:42 9223372030412324865

// test2(n) resultout
// 2019/03/12 17:38:20 9223372030412324865
// 2019/03/12 17:38:20 9223372030412324865
// 2019/03/12 17:38:20 9223372030412324865
// 2019/03/12 17:38:20 9223372030412324865
local storage
  • 与线程不同, goroutine 任务无法设置优先级, 无法获取编号, 没有局部存储, 甚至连返回值都会被抛弃, 但除优先级外, 其他功能都很容易实现.
Gosched
  • 暂停, 释放线程去执行其他任务. 当前任务被放回队列, 等待下次调试时恢复执行.
  • 该函数很少被使用, 因为运行时会主动向长时间运行的任务发出抢占调度
Goexit
  • 立即终止当前任务, 运行时确保所有已注册延迟调用被执行. 该函数不会影响其他并发任务, 不会引发panic, 自然也无法捕获
  • 如果在main.mian里调用Goexit, 它会等待其他任务结束,然后让进程直接崩溃.
  • 无论身处哪一层, Goexit都能立即终止整个调用堆栈, 这与return仅退出当前函数不同.
  • 标准库函数os.Exit 可终止进程, 但不会执行延迟调用.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值