博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
golang 线程与通道
阅读量:5054 次
发布时间:2019-06-12

本文共 2615 字,大约阅读时间需要 8 分钟。

首先我们来看线程,在golang里面也叫goroutine

在读这篇文章之前,我们需要了解一下并发与并行。golang的线程是一种并发机制,而不是并行。它们之间的区别大家可以上网搜一下,网上有很多的介绍。

下面我们先来看一个例子吧

import(

         "fmt"

)

funcmain(){

    go fmt.Println("1")
    fmt.Println("2")
}

在golang里面,使用go这个关键字,后面再跟上一个函数就可以创建一个线程。后面的这个函数可以是已经写好的函数,也可以是一个匿名函数

funcmain(){

        vari=3

    go func(a int) {
        fmt.Println(a)
        fmt.Println("1")
    }(i)
    fmt.Println("2")
  
}

上面的代码就创建了一个匿名函数,并且还传入了一个参数i,下面括号里的i是实参,a是形参。

那么上面的代码能按照我们预想的打印1、2、3吗?告诉你们吧,不能,程序只能打印出2。下面我把正确的代码贴出来吧

import(

    "fmt"
    "time"    
)

funcmain(){

    var i = 3
    go func(a int) {
        fmt.Println(a)
        fmt.Println("1")
    }(i)
    fmt.Println("2") time.Sleep(1 * time.Second)
}
我只是在最后加了一行让主线程休眠一秒的代码,程序就会依次打印出2、3、1。

那为什么会这样呢?因为程序会优先执行主线程,主线程执行完成后,程序会立即退出,没有多余的时间去执行子线程。如果在程序的最后让主线程休眠1秒钟,那程序就会有足够的时间去执行子线程。

线程先讲到这里,下面我们来看看通道吧。

通道又叫channel,顾名思义,channel的作用就是在多线程之间传递数据的。

创建无缓冲channel

chreadandwrite :=make(chan int)

chonlyread := make(<-chan int) //创建只读channel
chonlywrite := make(chan<- int) //创建只写channel

下面我们来看一个例子:

        ch :=make(chan int)     

ch <- 1
      go func() {
        <-ch
        fmt.Println("1")
      }()
      fmt.Println("2")

这段代码执行时会出现一个错误: :=make(chan int,1)

 

    ch <- 1
    go func() {
        v := <-ch
        fmt.Println(v)
    }()
    time.Sleep(1 * time.Second)
    fmt.Println("2")

这样的话程序就会依次打印出1、2

2、把ch<-1这一行代码放到子线程代码的后面,代码如下:

       ch :=make(chan int)

    go func() {
        v := <-ch
        fmt.Println(v)
    }()
    ch <- 1
    fmt.Println("2")

这里就不用让主线程休眠了,因为channel在主线程中被赋值后,主线程就会阻塞,直到channel的值在子线程中被取出。

最后我们看一个生产者和消费者的例子:

import (

    "fmt"
    "time"
)
func produce(p chan<- int) {
    for i := 0; i < 10; i++ {
        p <- i
        fmt.Println("send:", i)
    }
}
func consumer(c <-chan int) {
    for i := 0; i < 10; i++ {
        v := <-c
        fmt.Println("receive:", v)
    }
}
func main() {
    ch := make(chan int)
    go produce(ch)
    go consumer(ch)
    time.Sleep(1 * time.Second)
}

在这段代码中,因为channel是没有缓冲的,所以当生产者给channel赋值后,生产者这个线程会阻塞,直到消费者线程将channel中的数据取出。消费者第一次将数据取出后,进行下一次循环时,消费者的线程也会阻塞,因为生产者还没有将数据存入,这时程序会去执行生产者的线程。程序就这样在消费者和生产者两个线程间不断切换,直到循环结束。

下面我们再看一个带缓冲的例子:

import (

    "fmt"
    "time"
)
func produce(p chan<- int) {
    for i := 0; i < 10; i++ {
        p <- i
        fmt.Println("send:", i)
    }
}
func consumer(c <-chan int) {
    for i := 0; i < 10; i++ {
        v := <-c
        fmt.Println("receive:", v)
    }
}
func main() {
    ch := make(chan int, 10)
    go produce(ch)
    go consumer(ch)
    time.Sleep(1 * time.Second)
}

在这个程序中,缓冲区可以存储10个int类型的整数,在执行生产者线程的时候,线程就不会阻塞,一次性将10个整数存入channel,在读取的时候,也是一次性读取。

转载于:https://www.cnblogs.com/navysummer/p/8458620.html

你可能感兴趣的文章
【知识库】-数据库_MySQL 的七种 join
查看>>
.net 写文件上传下载webservice
查看>>
noSQL数据库相关软件介绍(大数据存储时候,必须使用)
查看>>
iOS开发——缩放图片
查看>>
HTTP之URL的快捷方式
查看>>
满世界都是图论
查看>>
配置链路聚合中极小错误——失之毫厘谬以千里
查看>>
代码整洁
查看>>
蓝桥杯-分小组-java
查看>>
Java基础--面向对象编程1(类与对象)
查看>>
Android Toast
查看>>
iOS开发UI篇—Quartz2D使用(绘制基本图形)
查看>>
docker固定IP地址重启不变
查看>>
桌面图标修复||桌面图标不正常
查看>>
JavaScript基础(四)关于对象及JSON
查看>>
关于js sort排序方法
查看>>
JAVA面试常见问题之Redis篇
查看>>
javascript:二叉搜索树 实现
查看>>
网络爬虫Heritrix源码分析(一) 包介绍
查看>>
__int128的实现
查看>>