golang协程同步的几种方法

目录

  • golang协程同步的几种方法

    • 协程概念简要理解
    • 为什么要做同步
    • 协程的几种同步方法
      • Mutex
      • channel
      • WaitGroup

golang协程同步的几种方法



本文简要介绍下go中协程的几种同步方法。

协程概念简要理解

协程类似线程,是一种更为轻量级的调度单位,但协程还是不同于线程的,线程是系统级实现的,常见的调度方法是时间片轮转法,如每隔10ms切换一个线程执行。

协程则是应用软件级实现,它和线程的原理差不多,当一个协程调度到另一个协程时,将上一个协程的上下文信息压入堆栈,来回切换。一个线程可以跑很多个协程,由这个线程来调度协程的切换,如果是C/C++的话底层就能通过select/poll/epoll来做,例如微信后台的开源libco库。

golang协程底层不是C,纯go实现,golang的协程应该是目前各类有协程概念的语言中实现的最完整和成熟的,调度是基于GPM模型实现的,有兴趣可以去了解下,这里不扯远了,下面看看协程的同步。

为什么要做同步

至于为什么需要同步呢,类似线程要做同步差不多,现在的cpu都是多核,假设一核一个线程同时一起访问同一块内存中的数据吗,那么可能上一ns第一个线程刚把数据从寄存器拷贝到内存,第二个线程马上又把此数据用它修改的值给覆盖了,这样共享数据变会乱套。

举个例子 :

用2个协程序并发各自增一个全局变量100 0000 次

package main 

import(
    "fmt"
    "time"
)

var share_cnt uint64 = 0

func incrShareCnt() {
    for i:=0; i < 1000000; i++ {
        share_cnt++
    }

    fmt.Println(share_cnt)
}

func main()  {

    for i:=0; i < 2; i++ {
        go incrShareCnt()
    }

    time.Sleep(1000*time.Second)

}

运行4次 , 可以看到我们虽然自增了200 0000次,但没有一个输出200 0000的结果.

协程的几种同步方法

Mutex

互斥锁,可以创建为其他结构体的字段;零值为解锁状态。Mutex类型的锁和线程无关,可以由不同的线程加锁和解锁。

package main

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

var share_cnt uint64 = 0

var lck sync.Mutex

func incrShareCnt() {
    for i:=0; i < 1000000; i++ {
        lck.Lock()
        share_cnt++
        lck.Unlock()
    }

    fmt.Println(share_cnt)
}

func main()  {

    for i:=0; i < 2; i++ {
        go incrShareCnt()
    }

    time.Sleep(1000*time.Second)

}

channel

使用golang的channel, 下面一个典型的生产消费模型

package main 

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

func main() {

    msg_chan := make(chan string)
    done     := make(chan bool)

    i := 0

    go func() {
        for  {
            i++
            time.Sleep(1*time.Second)
            msg_chan <- "on message"
            <- done
        }
    }()

    go func() {
        for {
            select {
            case msg := <- msg_chan :
                i++
                fmt.Println(msg + " " + strconv.Itoa(i))
                time.Sleep(2*time.Second)
                done <- true
            }
        }

    }()

    time.Sleep(20*time.Second)
}

WaitGroup

sync包中的WaitGroup可用等待一组协程的结束。
父协程通过Add方法来设定应等待的线程的数量。
每个被等待的协程在结束时调用Done方法。
同时,主协程里调用Wait方法阻塞至所有线程结束。

package main

import(
    "sync"
    "net/http"
)

var wg sync.WaitGroup
var urls = []string{
    "http://www.baidu.com/",
    "http://www.taobao.com/",
    "http://www.tianmao.com/",
}
func main() {

for _, url := range urls {
    // Increment the WaitGroup counter.
    wg.Add(1)
    // Launch a goroutine to fetch the URL.
    go func(url string) {
        // Decrement the counter when the goroutine completes.
        defer wg.Done()
        // Fetch the URL.
        http.Get(url)
    }(url)
}
// Wait for all HTTP fetches to complete.
wg.Wait()

}

原文地址:https://www.cnblogs.com/ailumiyana/p/11107835.html

时间: 2024-10-10 18:27:33

golang协程同步的几种方法的相关文章

C#线程同步的几种方法

我们在编程的时候,有时会使用多线程来解决问题,比如你的程序需要在后台处理一大堆数据,但还要使用户界面处于可操作状态:或者你的程序需要访问一些外部资源如数据库或网络文件等.这些情况你都可以创建一个子线程去处理,然而,多线程不可避免地会带来一个问题,就是线程同步的问题.如果这个问题处理不好,我们就会得到一些非预期的结果. 在网上也看过一些关于线程同步的文章,其实线程同步有好几种方法,下面我就简单的做一下归纳. 一.volatile关键字 volatile是最简单的一种同步方法,当然简单是要付出代价的

归纳一下:C#线程同步的几种方法

转自原文 归纳一下:C#线程同步的几种方法 我们在编程的时候,有时会使用多线程来解决问题,比如你的程序需要在后台处理一大堆数据,但还要使用户界面处于可操作状态:或者你的程序需要访问一些外部资源如数据库或网络文件等.这些情况你都可以创建一个子线程去处理,然而,多线程不可避免地会带来一个问题,就是线程同步的问题.如果这个问题处理不好,我们就会得到一些非预期的结果. 在网上也看过一些关于线程同步的文章,其实线程同步有好几种方法,下面我就简单的做一下归纳. 一.volatile关键字 volatile是

Linux下线程同步的几种方法

Linux下提供了多种方式来处理线程同步,最常用的是互斥锁.条件变量和信号量. 一.互斥锁(mutex) 锁机制是同一时刻只允许一个线程执行一个关键部分的代码.  1. 初始化锁 int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutex_attr_t *mutexattr); 其中参数 mutexattr 用于指定锁的属性(见下),如果为NULL则使用缺省属性. 互斥锁的属性在创建锁的时候指定,在LinuxThreads实

Python与Golang协程异同

背景知识 这里先给出一些常用的知识点简要说明,以便理解后面的文章内容. 进程的定义: 进程,是计算机中已运行程序的实体.程序本身只是指令.数据及其组织形式的描述,进程才是程序的真正运行实例. 线程的定义: 操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位. 进程和线程的关系: 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务. CPU的最小调度单元是线程不是进程,所以单进程多线程也可以利用多核CPU. 协程的定义: 协

面试必问:Golang高阶-Golang协程实现原理

引言 实现并发编程有进程,线程,IO多路复用的方式.(并发和并行我们这里不区分,如果CPU是多核的,可能在多个核同时进行,我们叫并行,如果是单核,需要排队切换,我们叫并发) 进程和线程的区别 进程是计算机资源分配的最小单位,进程是对处理器资源(CPU),虚拟内存(1)的抽象, 虚拟内存是对主存资源(Memory)和文件(2)的抽象,文件是对I/O设备的抽象. 虚拟内存是操作系统初始化后内部维护的一个程序加载空间,对于32位操作系统来说,也就是寄存器有32位的比特长度,虚拟内存中每个字节都有一个内

web集群时session同步的3种方法

web集群时session同步的3种方法 2011-09-21 15:53:30 标签:会话保持 会话同步 session同步 集群会话同步 在做了web集群后,你肯定会首先考虑session同步问题,因为通过负载均衡后,同一个IP访问同一个页面会被分配到不同的服务器上,如果session不同步的话,一个登录用户,一会是登录状态,一会又不是登录状态.所以本文就根据这种情况给出三种不同的方法来解决这个问题: 一,利用数据库同步session 在做多服务器session同步时我没有用这种方法,如果非

线程同步的几种方法的总结

线程同步的方式包括:互斥锁.读写锁.条件变量.信号量和令牌. 互斥锁和读写锁:提供对临界资源的保护,当多线程试图访问临界资源时,都必须通过获取锁的方式来访问临界资源.(临界资源:是被多线程共享的资源)当读写线程获取锁的频率差别不大时,一般采用互斥锁,如果读线程访问临界资源的频率大于写线程,这个时候采用读写锁较为合适,读写锁允许多个读线程同时访问临界资源,读写线程必须互斥访问临界资源.读写锁的实现采用了互斥锁,所以在读写次数差不多的情况下采用读写锁性能没有直接采用互斥锁来的高. 条件变量:提供线程

web集群时session同步的3种方法[转]

在做了web集群后,你肯定会首先考虑session同步问题,因为通过负载均衡后,同一个IP访问同一个页面会被分配到不同的服务器上,如果session不同步的话,一个登录用户,一会是登录状态,一会又不是登录状态.所以本文就根据这种情况给出三种不同的方法来解决这个问题: 一,利用数据库同步session 1,用一个低端电脑建个数据库专门存放web服务器的session,或者,把这个专门的数据库建在文件服务器上,用户访问web服务器时,会去这个专门的数据库check一下session的情况,以达到se

关于协程:nodejs和golang协程的不同

nodejs和golang都是支持协程的,从表现上来看,nodejs对于协程的支持在于async/await,golang对协程的支持在于goroutine.关于协程的话题,简单来说,可以看作是非抢占式的轻量级线程. 协程本身 一句话概括,上面提到了 "可以看作是非抢占式的轻量级线程". 在多线程中,把一段代码放在一个线程中执行,cpu会自动将代码分成碎片,并在一定时间切换cpu控制权,线程通过锁机制确保自己使用的资源在cpu执行别的线程的代码时被修改(占用的内存堆栈.硬盘数据资源等)