1. go协程(go routine)

go原生支持并发:goroutine和channel。

go协程是与其他函数或方法一起并发运行的函数和方法。go协程可以看作是轻量级线程。

调用函数或者方法时,在前面加上关键字go,可以让一个新的GO协程并发地运行。

  • l  启动一个新的协程时,协程的调用会立即返回。与函数不同,程序控制不会去等待 Go 协程执行完毕。在调用 Go 协程之后,程序控制会立即返回到代码的下一行,忽略该协程的任何返回值。
  • l  如果希望运行其他 Go 协程,Go 主协程必须继续运行着。如果 Go 主协程终止,则程序终止,于是其他 Go 协程也不会继续运行。

2. 信道channel

信道可以想象成Go协程之间通信的管道。

chan T 表示 T 类型的信道。信道与类型相关,只能运输该类型的数据。

信道的零值为 nil。信道的零值没有什么用,应该像对 map 和切片所做的那样,用 make 来定义信道。

data := <- a // 读取信道 a
a <- data // 写入信道 a

信道发送与接收默认是阻塞的。

信道会产生死锁。当Go协程给一个信道发送数据,而没有信道接收数据时,程序触犯panic,形成死锁。

信道数据都是单传递的,当一个接收信道收到数据后,其他信道接收者就不能接收到相同数据,即信道数据只能由任意一个且仅有一个信道接收者获取到。

单向信道

sendch := make(chan<- int)    // 定义单向信道,定义只写数据的信道,<-指向chan
只写通道:chan<- T
只读通道:<-chan T

可以把一个双向信道转换成唯送信道或者唯收信道(send only or receive only),但反过来不可以。

package main

import (
"fmt"
"time"
"os"
) func main(){ data := make(chan int)
go func(out chan<- int){
time.Sleep(* time.Second)
out <-
}(data) <- data fmt.Println("Receive data, first") go func(out <-chan int){
time.Sleep( * time.Second)
<-out
fmt.Println("Receive data, Second")
os.Exit()
}(data) data <-
for {
time.Sleep( * time.Second)
}
}

single channel

关闭信道

close(ch)

数据发送方可以关闭信道,通知接收方这个信道不再有数据发送过来。

当从信道接收数据时,接收方可以多用一个变量来检查信道是否已经关闭。

v, ok := <- ch

如果可以从信道接收数据,ok等于true;如果信道关闭,ok等于false。

所有的channel接收者都会在channel关闭时,立刻从阻塞等待中返回且上述ok值为false。这个广播机制常被利用,向多个订阅者同时发送信号。如:退出信号。

从一个关闭的信道中读取到的值时该信道类型的零值。向关闭的channel发送数据,会导致panic。

range遍历信道

for range 循环用于在一个信道关闭之前,从信道接收数据。

    ch := make(chan int)
go producer(ch)
for v := range ch {
fmt.Println("Received ",v)
}

3. 缓冲信道

信道的接收和发送都是阻塞的,当多个信道发送者向一个信道发送数据时,接收信道在接收一个信道发送的数据后处理其他任务(不再处理该信号数据),会导致其他发送信道协程阻塞,造成携程泄露。

runtime.NumGoroutine()   // 获取当前协程数

buffered Channel可以接收多个信道数据,从而排除阻塞。(仅需任意任务完成即可)

buffered Channel只有缓冲已满的情况才会阻塞发送数据,同样只有缓冲为空时才阻塞接收数据。

ch := make(chan type, capacity)   // capacity大于0

缓冲信道的容量是指信道可以存储的值的数量。缓冲信道的长度是指信道中当前排队的元素个数。

4.工作池

WaitGroup 用于等待一批 Go 协程执行结束。程序控制会一直阻塞,直到这些协程全部执行完毕。

定义:var wg sync.WaitGroup

调用:wg.Add(1)…wg.Done()…wg.Wait()

package main

import (
"fmt"
"sync"
"time"
) func process(i int, wg *sync.WaitGroup) {
fmt.Println("started Goroutine ", i)
time.Sleep( * time.Second)
fmt.Printf("Goroutine %d ended\n", i)
wg.Done()
} func main() {
no :=
var wg sync.WaitGroup
for i := ; i < no; i++ {
wg.Add()
go process(i, &wg)
}
wg.Wait()
fmt.Println("All go routines finished executing")
} output:
started Goroutine
started Goroutine
started Goroutine
Goroutine ended
Goroutine ended
Goroutine ended
All go routines finished executing

协程中传参wg地址非常重要,wg.Done()执行完毕后主协程才知道。若是值拷贝,main函数不知道。

package main

import (
"fmt"
"math/rand"
"sync"
"time"
) type Job struct {
id int
randomno int
} type Result struct{
job Job
sumofdigits int
} var jobs = make(chan Job, )
var results = make(chan Result, ) func sum_digits(number int)int {
sum :=
for number != {
i:= number%
sum += i
number = number/
}
time.Sleep(*time.Second)
return sum
}
func worker(wg *sync.WaitGroup){
for job := range jobs{
output := Result{job, sum_digits(job.randomno)}
results <- output
} wg.Done()
} func create_worker_pool(num_workers int){
var wg sync.WaitGroup
for i:=; i < num_workers; i++{
wg.Add()
go worker(&wg)
}
wg.Wait()
close(results)
} func allocate(num_jobs int){
for i := ; i < num_jobs; i++{
randomno := rand.Intn()
job := Job{i, randomno}
jobs <- job
}
close(jobs)
}
func result(done chan bool){
for result := range results{
fmt.Printf("Job id %d, input random no %d, sum of digits %d\n", result.job.id, result.job.randomno, result.sumofdigits)
}
done <- true
} func main(){
startTime := time.Now()
num_jobs :=
go allocate(num_jobs)
done := make(chan bool)
go result(done)
num_workers :=
create_worker_pool(num_workers)
<-done
endTime := time.Now()
diff := endTime.Sub(startTime)
fmt.Println("total time taken ", diff.Seconds(), "seconds")
}

5.select

select 语句用于在多个发送/接收信道操作中进行选择。

select 语句会一直阻塞,直到发送/接收操作准备就绪。如果有多个信道操作准备完毕,select 会随机地选取其中之一执行。该语法与 switch 类似,所不同的是,这里的每个 case 语句都是信道操作。

在没有 case 准备就绪时,可以执行 select 语句中的默认情况(Default Case)(default立即返回)。

可通过time.After()设置超时处理,这通常用于防止 select 语句一直阻塞。

package main

import (
"fmt"
"time"
) func server1(ch chan string) {
time.Sleep( * time.Second)
ch <- "from server1"
}
func server2(ch chan string) {
time.Sleep( * time.Second)
ch <- "from server2" }
func main() {
output1 := make(chan string)
output2 := make(chan string)
go server1(output1)
go server2(output2)
select {
case s1 := <-output1:
fmt.Println(s1)
case s2 := <-output2:
fmt.Println(s2)
case <- time.After(time.Second * 5)
fmt.Println("Timeout")
// default:
// fmt.Println("No one returned")
}
}

select应用:假设我们有一个关键性应用,需要尽快地把输出返回给用户。这个应用的数据库复制并且存储在世界各地的服务器上。我们向两台服务器发送请求,并使用 select 语句等待相应的信道发出响应。select 会选择首先响应的服务器,而忽略其它的响应。使用这种方法,我们可以向多个服务器发送请求,并给用户返回最快的响应了。

package main

func main() {
select {}
}

select 语句没有任何 case,因此它会一直阻塞,导致死锁。该程序会触发 panic。

6.mutex

Mutex 用于提供一种加锁机制(Locking Mechanism),可确保在某时刻只有一个协程在临界区运行,以防止出现竞态条件。

var mutex sync.Mutex
mutex.Lock()
x = x +
mutex.Unlock()

信道处理竞态条件

    ch <- true
x = x +
<- ch

当 Go 协程需要与其他协程通信时,可以使用信道。而当只允许一个协程访问临界区时,可以使用 Mutex。

7. once

sync.Once可以控制函数只能被调用一次,不会被多次重复调用。

func (o *Once) Do(f func())
import (
"sync"
) type Watchers struct {
devices map[string] string
} var (
wcOnce sync.Once
watchers *Watchers
) // Create a singleton WatcherCache instance
func newWatchers() *Watchers {
wcOnce.Do(func() {
watchers = &Watchers{}
}) return watchers
}

sync.Once.Do(f func())能保证once只执行一次,无论之后是否更换once.Do(xx)里的方法。

package main

import (
"fmt"
"sync"
) func main(){
var once sync.Once
once.Do(func(){
fmt.Println("Test sync Once")
})
}

参考:

1. https://studygolang.com/subject/2

2.  Go并发编程实践

golang并发基础的更多相关文章

  1. GoLang之基础

    GoLang之基础 Go是一种并发的.带垃圾回收的.快速编译的语言. 经典的"hello world"入门: package main import "fmt" ...

  2. Golang并发原理及GPM调度策略(一)

    其实从一开始了解到go的goroutine概念就应该想到,其实go应该就是在内核级线程的基础上做了一层逻辑上的虚拟线程(用户级线程)+ 线程调度系统,如此分析以后,goroutine也就不再那么神秘了 ...

  3. JAVA多线程和并发基础面试问答(转载)

    JAVA多线程和并发基础面试问答 原文链接:http://ifeve.com/java-multi-threading-concurrency-interview-questions-with-ans ...

  4. [转] JAVA多线程和并发基础面试问答

    JAVA多线程和并发基础面试问答 原文链接:http://ifeve.com/java-multi-threading-concurrency-interview-questions-with-ans ...

  5. JAVA多线程和并发基础面试问答

    转载: JAVA多线程和并发基础面试问答 多线程和并发问题是Java技术面试中面试官比较喜欢问的问题之一.在这里,从面试的角度列出了大部分重要的问题,但是你仍然应该牢固的掌握Java多线程基础知识来对 ...

  6. 【多线程】JAVA多线程和并发基础面试问答(转载)

    JAVA多线程和并发基础面试问答 原文链接:http://ifeve.com/java-multi-threading-concurrency-interview-questions-with-ans ...

  7. Java 并发基础

    Java 并发基础 标签 : Java基础 线程简述 线程是进程的执行部分,用来完成一定的任务; 线程拥有自己的堆栈,程序计数器和自己的局部变量,但不拥有系统资源, 他与其他线程共享父进程的共享资源及 ...

  8. golang并发编程

    golang并发编程 引子 golang提供了goroutine快速实现并发编程,在实际环境中,如果goroutine中的代码要消耗大量资源时(CPU.内存.带宽等),我们就需要对程序限速,以防止go ...

  9. (转)JAVA多线程和并发基础面试问答

    JAVA多线程和并发基础面试问答 原文链接:http://ifeve.com/java-multi-threading-concurrency-interview-questions-with-ans ...

随机推荐

  1. 如何上传本地jar至远程仓库供其他项目使用

    我们首先需要创建自己内部nexus私服仓库.这里假设你已经做好了. 其次我们要明白nexus上如下几个component的用处. maven-central:maven中央库,默认从https://r ...

  2. Superset配置mysql数据源

    1.添加mysql数据源 测试连接的时候遇到 No module named 'MySQLdb'" 安装mysqlclient pip install mysqlclient 如果遇到 ER ...

  3. unittest===unittest 的几种执行方式

    #demo.py import requests import json class RunMain: def __init__(self, url, method, data=None): self ...

  4. nginx过滤access_log中HEAD、OPTIONS请求记录

    网上很多教程说是这样做: if ($request_method = HEAD) { access_log off; } 试了之后是不行的,正确的做法如下: http { map $request_m ...

  5. vue脚手架中使用Vant,实现自动按需引入组件,并将px转换为rem

    偶然间看到一款不错的移动端vue组件库Vant,照着官方文档敲了一下,感觉还是不错的.想着以后的项目中可能会运用到,特此记录下,方便之后使用. 现在很多的组件库为了减小代码包体积,都支持按需加载了.V ...

  6. Xamarin.Android UnauthorizedAccessException: Access to the path is denied

    进行文件读写,勾选了权限 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" / ...

  7. c#实现定时任务(Timer)

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  8. SQL Server 数据库启动过程(用户数据库加载过程的疑难杂症)

    前言 本篇主要是上一篇文章的补充篇,上一篇我们介绍了SQL Server服务启动过程所遇到的一些问题和解决方法,可点击查看,我们此篇主要介绍的是SQL Server启动过程中关于用户数据库加载的流程, ...

  9. Unity 新手引导

    根据Shader动态生成遮罩 源码地址 圆形遮罩镂空处理脚本: using System; using System.Collections.Generic; using UnityEngine; u ...

  10. Delphi阿里云短信【支持短信发送、短信批量发送和查询短信发送记录】

    作者QQ:(648437169) 点击下载➨Delphi阿里云短信             阿里云api文档 [Delphi 阿里云短信]是最新的阿里云短信接口,不是阿里大于短信接口,支持SendSm ...