Golang——8、协程和管道

1、协程

1.1、进程、线程和协程

进程(Process)就是程序在操作系统中的一次执行过程,是系统进行资源分配和调度的基本单位,进程是一个动态概念,是程序在执行过程中分配和管理资源的基本单位,每一个进程都有一个自己的地址空间。一个进程至少有 5 种基本状态,它们是:初始态,执行态,等待状态,就绪状态,终止状态。

线程是进程的一个执行实例,是程序执行的最小单元,它是比进程更小的能独立运行的基本单位

并发:多个线程同时竞争一个位置,竞争到的才可以执行,每一个时间段只有一个线程在执行。
并行:多个线程可以同时执行,每一个时间段,可以有多个线程同时执行。

通俗的讲多线程程序在单核CPU上面运行就是并发,多线程程序在多核CUP上运行就是并行,如果线程数大于CPU核数,则多线程程序在多个CPU上面运行既有并行又有并发。

Golang中的协程:
Golang中的主线程:(可以理解为线程/也可以理解为进程),在一个Golang程序的主线程上可以起多个协程。Golang 中多协程可以实现并行或者并发。
协程:可以理解为用户级线程,这是对内核透明的,也就是系统并不知道有协程的存在,是完全由用户自己的程序进行调度的。Golang的一大特色就是从语言层面原生支持协程,在函数或者方法前面加go关键字就可创建一个协程。可以说Golang中的协程就是goroutine 。

Golang 中的多协程有点类似其他语言中的多线程。
多协程和多线程:Golang 中每个goroutine (协程) 默认占用内存远比Java 、C的线程少。
OS线程(操作系统线程)一般都有固定的栈内存(通常为 2MB 左右),一个goroutine (协程)占用内存非常小,只有 2KB 左右,多协程 goroutine切换调度开销方面远比线程要少。


1.2、goroutine的使用以及sync.WaitGroup

下面实现创建一个协程,在协程和主线程中分别执行打印语句,每次休眠一秒。

package main

import (
	"fmt"
	"time"
)

func test() {
   
   
	for i := 0; i < 3; i++ {
   
   
		fmt.Println("test...")
		time.Sleep(time.Second)
	}
}

func main() {
   
   
	go test()
	for i := 0; i < 3; i++ {
   
   
		fmt.Println("main...")
		time.Sleep(time.Second)
	}
}

在这里插入图片描述


但是有个问题,如果主线程执行的速度比较快呢,我们可以修改一下代码,让主线程跑快一些。
在这里插入图片描述
此时我们发现主线程执行完后,协程不会再继续执行了。这是因为主线程执行完后整个程序就退出了。
所以我们需要使用sync.WaitGroup来让主线程等待协程。

package main

import (
	"fmt"
	"sync"
	"time"
)

var wg sync.WaitGroup

func test() {
   
   
	for i := 0; i < 3; i++ {
   
   
		fmt.Println("test...")
		time.Sleep(time.Second)
	}
	wg.Done()
}

func main() {
   
   
	wg.Add(1)
	go test()
	for i := 0; i < 3; i++ {
   
   
		fmt.Println("main...")
		time.Sleep(100)
	}
	wg.Wait()
}

在这里插入图片描述
可以看到此时主线程执行完后会等待协程执行完,然后才会退出。有点类似于创建进程/线程并进行进程/线程等待回收。
其中:sync.WaitGroup本质上是一个计数器,Add方法表示增加计数器,Done表示让计数器减1,Wait表示等待协程执行完毕。


1.3、启动多个协程

package main

import (
	"fmt"
	"sync"
)

var wg sync.WaitGroup

func test(id int) {
   
   
	for i := 1; i <= 3; i++ {
   
   
		fmt.Printf("我是协程[%v]..., i=%d\n", id, i)
	}
	wg.Done()
}

func main() {
   
   
	for i := 1; i <= 5; i++ {
   
   
		wg.Add(1)
		go test(i)
	}
	wg.Wait()
}

在这里插入图片描述


1.4、设置Golang并行运行的时候占用的cup数量

可以使用runtime.NumCPU()来获取当前计算机上CPU核心的数量。

Go运行时的调度器使用GOMAXPROCS参数来确定需要使用多少个 OS 线程来同时执行Go代码。默认值是机器上的 CPU 核心数。 例如在一个 8 核心的机器上,调度器会把 Go 代码同时调度到8个OS线程上。
Go语言中可以通过runtime.GOMAXPROCS()函数设置当前程序并发时占用的CPU逻辑核心数。

package main

import (
	"fmt"
	"runtime"
)

func main() {
   
   
	num := runtime.NumCPU()
	fmt.Println("CPU数量为:", num)
	runtime.GOMAXPROCS(num - 1)
}

1.5、goroutine统计素数

现在假设要统计1->50000000中有多少素数,最普遍的做法是使用一个for循环来做,如下:

package main

import (
	"fmt"
	"time"
)

func main() {
   
   
	u1 := time.Now().Unix()
	// var cnt = 0
	for i := 2; i <= 50000000; i++ {
   
   
		flag := true
		for j := 2; j*j <= i; j++ {
   
   
			if i%j == 0 {
   
   
				flag = false
				break
			}
		}
		if flag {
   
   
			// cnt++
		}
	}
	// fmt.Println("共有素数:", cnt)
	u2 := time.Now().Unix()
	fmt.Println("花费时间:", u2-u1)
}

在这里插入图片描述
我们发现运行时间高达12S。下面我们使用goroutine试试看:
我们创建5个协程来完成,每个协程处理一千万个数据。

package main

import (
	"fmt"
	"sync"
	"time"
)

var wg sync.WaitGroup

func test(x int) {
   
   
	for i := (x-1)*10000000 + 1; i <= x*10000000; i++ {
   
   
		if i == 1 {
   
   
			continue
		}
		flag := true
		for j := 2; j*j <= i; j++ {
   
   
			if i%j == 0 {
   
   
				flag = false
				break
			}
		}
		if flag {
   
   

		}
	}
	wg.Done()
}

func main() {
   
   
	u1 := time.Now().Unix()
	for i := 1; i <= 5; i++ {
   
   
		wg.Add(1)
		go test(i)
	}
	wg.Wait()
	u2 := time.Now().Unix()
	fmt.Println("花费时间:", u2-u1)</
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值