golang总结-并发

目录

  • 2.7 并发编程

    • go协程
    • go管道

2.7 并发编程

go协程

golang 通过一个go关键字就可以开启一个协程。

func main() {
    //两个交错输出
    go sayHello()
    go sayHello2()
    time.Sleep(time.Second * 3) //阻塞主线程
}

func sayHello() {
    for i := 0; i < 30; i++ {
        fmt.Println("hello world")
    }
}

func sayHello2() {
    for i := 0; i < 30; i++ {
        fmt.Println("你好中国")
    }
}
//通过sync.WaitGroup来等待所有线程完成
package main

import (
    "fmt"
    "sync"
)

func main() {
    var w = &sync.WaitGroup{}
    w.Add(2)
    go sayEn(w)
    go sayZh(w)
    w.Wait()
}

func sayEn(w *sync.WaitGroup) {
    for i := 0; i < 30; i++ {
        fmt.Println("hello world")
    }
    w.Done() //每当这个方法完成则减少1
}

func sayZh(w *sync.WaitGroup) {
    for i := 0; i < 30; i++ {
        fmt.Println("中国你好")
    }
    w.Done() //每当这个方法完成则减少1
}

go管道

管道的定义:

//无缓冲管道
flag := make(chan bool)
//有缓冲管道
data := make(chan int, 10)
//向管道中添加值
data <- 10
//从管道中取值
agr := <- data
<- data //也可以直接释放值,不用变量接收

1. 通过go实现同步

package main

import (
    "fmt"
)

func main() {
    w1, w2 := make(chan bool), make(chan bool)
    go sayEn_chan(w1)
    go sayZh_chan(w2)
    <- w1 //阻塞,直到chan 可以取出数据
    <- w2
}

func sayEn_chan(w chan bool) {
    for i := 0; i < 30; i++ {
        fmt.Println("hello world")
    }
    w <- true //方法完成写入通道
}

func sayZh_chan(w chan bool) {
    for i := 0; i < 30; i++ {
        fmt.Println("中国你好")
    }
    w <- true
}

2. 正确处理累加

package main

import (
    "fmt"
    "sync/atomic"
)

var (
    count int64
)

func main() {
    w1, w2 := make(chan bool), make(chan bool)
    go add(w1)
    go add(w2)
    <- w1 //阻塞,直到chan 可以取出数据
    <- w2
    fmt.Println(count)
}

func add(w chan bool) {
    for i := 0; i < 5000; i++ {
        atomic.AddInt64(&count, 1)
    }
    w <- true
}

3. 通道实现数据共享

package main

import (
    "fmt"
    "math/rand"
    "sync"
)

var wg sync.WaitGroup

func main() {
    count := make(chan int)
    wg.Add(2)
    go player("张三", count)
    go player("李四", count)
    //发球
    count <- 1
    wg.Wait() //阻塞等待2个线程完成
}

func player(name string, count chan int) {
    defer wg.Done()

    for {
        i, ok := <-count

        if !ok { //通道关闭
            fmt.Printf("运动员 %s 赢了\n", name)
            return
        }

        tmp := rand.Intn(100)
        if tmp % 13 == 0 { //没有接到球
            fmt.Printf("运动员 %s 输了\n", name)
            close(count)
            return
        }
        fmt.Printf("运动员 %s 击球 %d \n", name , i)
        i ++
        count <- i
    }
}

4. 缓冲管道

package main

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

var (
    numberTasks = 10
    workers = 4
)

var wg2 sync.WaitGroup

func main() {
    wg2.Add(workers)
    tasks := make(chan int, numberTasks)

    for i := 0; i < workers; i++ {
        go work(tasks, i)
    }

    for j := 1; j <= numberTasks; j++ {
        tasks <- j
    }
    close(tasks)

    wg2.Wait()
}

func work(tasks chan int, worker int) {
    defer wg2.Done()
    for {
        task, ok := <- tasks
        if !ok {
            fmt.Printf("任务完成,工号:%d\n", worker)
            return
        }
        fmt.Printf("工号:%d, 开始工作:%d\n", worker, task)
        time.Sleep(time.Microsecond * 100)
        fmt.Printf("工号:%d, 完成工作:%d\n", worker, task)

    }

}

5. select

select 的特点是:不会阻塞,哪个管道有值,我取哪个。所以,下面当运行到go的时候,a,b还没有添值,所以只能选择defaul运行,这里可以把defualt部分和b<-2去掉,select会被阻塞,直到a<-1执行

func main() {
    a := make(chan int)
    b := make(chan int)
    go func() {
        b <- 2
        time.Sleep(time.Second * 3)
        a <- 1
    }()
    select {
    case <- a:
        fmt.Println("a")
    case <- b:
        fmt.Println("b")
        time.Sleep(time.Second * 3)
    default:
        fmt.Println("hello world")
    }
}

6. runner并发模型

package runner

import (
    "errors"
    "os"
    "os/signal"
    "time"
)

type Runner struct {
    interrupt chan os.Signal

    complete chan error

    timeout <-chan time.Time //声明一个只读的管道

    tasks []func(int)
}

var ErrorTimeout = errors.New("receive timeout")

var ErrorInterrupt = errors.New("interrupt error")

func New(duration time.Duration) *Runner {
    return &Runner{
        interrupt: make(chan os.Signal, 1),
        complete: make(chan error),
        timeout: time.After(duration),
    }
}

func (r *Runner) Add(tasks...func(int)) {
    r.tasks = append(r.tasks, tasks...)
}

func (r *Runner) getInterrupt() bool {
    select {
    case <-r.interrupt:
        signal.Stop(r.interrupt)
        return true
    default:
        return false
    }
}

func (r *Runner) run() error {
    for id, task := range r.tasks {
        if r.getInterrupt() {
            return ErrorInterrupt
        }
        task(id)
    }
    return nil
}

func (r *Runner) Start() error {
    signal.Notify(r.interrupt, os.Interrupt)
    go func() {
        r.complete <- r.run()
    }()

    select {
    case err := <- r.complete:
        return err
    case <- r.timeout:
        return ErrorTimeout
    }
}

测试

package main

import (
    "gorounting/runner"
    "log"
    "os"
    "time"
)

const (
    timeout  = 4 * time.Second
)

func main() {
    log.Println("任务开始")
    ru := runner.New(timeout)
    ru.Add(createTask(), createTask(), createTask(), createTask())

    if err := ru.Start(); err != nil {
        switch err {
        case runner.ErrorInterrupt:
            log.Println("系统被中断")
            os.Exit(1)
        case runner.ErrorTimeout:
            log.Println("系统超时")
            os.Exit(2)

        }
    }
    log.Println("程序结束")

}

func createTask() func(int) {
    return func(id int) {
        log.Printf("process-task #%d\n", id)
        time.Sleep(time.Duration(id) * time.Second )
    }
}

原文地址:https://www.cnblogs.com/hu1056043921/p/10355536.html

时间: 2024-08-14 00:26:37

golang总结-并发的相关文章

golang的并发

Golang的并发涉及二个概念: goroutine channel goroutine由关键字go创建. channel由关键字chan定义 channel的理解稍难点, 最简单地, 你把它当成Unix中的双向通道Pipe. 1. channel的定义 2. select阻塞 3. 缓存机制: 使用make()创建. 4. 超时机制: 使用time.After()函数. func main() {var abc chan intselect {case <-abc:fmt.Println(&quo

Golang之并发篇

进程和线程 A.进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位. B.线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位. C.一个进程可以创建和撤销多个线程:同一进程中的多个线程之间可以并发执行. 并发和并行 并发:多线程程序在一个核的cpu上运行 并行:多线程程序在多个核的cpu上运行举例..一个妈给一个碗给多个小孩喂饭,,是并发 一个妈给每个小孩一人一个碗,就是并行 并发  并行 协程和线程 协程:独立的栈空间,共享堆空

golang高并发的理解

前言 GO语言在WEB开发领域中的使用越来越广泛,Hired 发布的<2019 软件工程师状态>报告中指出,具有 Go 经验的候选人是迄今为止最具吸引力的.平均每位求职者会收到9 份面试邀请. 想学习go,最基础的就要理解go是怎么做到高并发的. 那么什么是高并发? 高并发(High Concurrency)是互联网分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计保证系统能够同时并行处理很多请求. 严格意义上说,单核的CPU是没法做到并行的,只有多核的CPU才能做到严格意义上的并行

sync:与golang的并发息息相关的包

楔子 我们知道golang除了兼顾了开发速度和运行效率之外,最大的亮点就是在语言层面原生支持并发,也就是通过所谓的goroutine.不过既然是并发,那么就势必会面临很多问题.比如:资源竞争,多个goroutine同时访问一个资源会发生竞争从而产生意想不到的结果,那么这时候我们会通过加锁来解决:主goroutine不能先退出,这时候我们会等待子goroutine.还有单例模式,以及对象池等等.那么golang是如何实现的呢?就是通过下面我们要介绍的sync包. sync.Mutex sync.M

golang语言并发与并行——goroutine和channel的详细理解(一)

如果不是我对真正并行的线程的追求,就不会认识到Go有多么的迷人. Go语言从语言层面上就支持了并发,这与其他语言大不一样,不像以前我们要用Thread库 来新建线程,还要用线程安全的队列库来共享数据. 以下是我入门的学习笔记. Go语言的goroutines.信道和死锁 goroutine Go语言中有个概念叫做goroutine, 这类似我们熟知的线程,但是更轻. 以下的程序,我们串行地去执行两次loop函数: func loop() { for i := 0; i < 10; i++ { f

golang map并发读写异常导致服务崩溃

昨天突然接到报警说服务端口丢失,也就是服务崩溃了. 1, 先看错误日志,发现是调用json.Marshal时出错了,错误原因是:concurrent map iteration and map write,即并发读写map. fatal error: concurrent map iteration and map write goroutine 543965 [running]: runtime.throw(0xb74baf, 0x26) /usr/local/go1.10.1/src/run

golang 实现并发计算文件数量

package main import ( "fmt" "io/ioutil" "os" ) func listDir(path string, ch chan int) { fmt.Println("waiting ..... read path:" + path) files, _ := ioutil.ReadDir(path) FileSlice := []string{} DirSlice := []string{}

golang版并发爬虫

准备爬取内涵段子的几则笑话,先查看网址:http://www.budejie.com/text/ 简单分析后发现每页的url呈加1趋势 第一页: http://www.budejie.com/text/1 第二页:http://www.budejie.com/text/2 ... 每页的段子: <a href="/detail-28278217.html"> 内容</a> <a href="/detail-28270675.html"&

进一步认识golang中的并发

如果你成天与编程为伍,那么并发这个名词对你而言一定特别耳熟.需要并发的场景太多了,例如一个聊天程序,如果你想让这个聊天程序能够同时接收信息和发送信息,就一定会用到并发,无论那是什么样的并发. 并发的意义就是:让一个程序同时做多件事情! 理解这一点非常重要,是的,并发的目的只是为了能让程序同时做另一件事情而已,并发的目的并不是让程序运行的更快(如果是多核处理器,而且任务可以分成相互独立的部分,那么并发确实可以让事情解决的更快).记得我学C++那时候开始接触并发,还以为每开一个线程程序就会加速一倍呢