专栏首页Golang语言社区GO语言高并发学习心得体会例

GO语言高并发学习心得体会例

<div class="blockcode"><blockquote>信号



sigRecv1:=make(chan os.Signal,1)

sigs1:=[]os.Signal{syscall.SIGINT,syscall.SIGQUIT}

signal.Notify(sigRecv1,sigs1...)





sigRecv2:=make(chan os.Signal,1)

sigs2:=[]os.Signal{syscall.SIGINT,syscall.SIGQUIT}

signal.Notify(sigRecv2,sigs2...)



// 接着我们用两个for循环来接收消息.考虑到主程序会退出,所以用waitgroup等待完成后退出.



var wg sync.WaitGroup

wg.Add(2)

go func(){

    for sig:=range sigRecv1{

        fmt.Printf("Received a signal from sigRecv1%v",sig)

    }

    fmt.Printf("End. [sigRecv1]\n")

}()





go func(){

    for sig:=range sigRecv2{

        fmt.Printf("Received a signal from sigRecv1%v",sig)

    }

    fmt.Printf("End. [sigRecv2]\n")

}()



wg.Wait()



int socket(int domain,int type,int protocol) 

image

socket type 类型 

image



TCP UDP



tcp 连结类型



image

可能会传回io.EOF,表明连接结束



var dataBuffer bytes.Buffer

b:=make([byte,10])



for {



    n,err:=conn.Read(b)

    if err!=nil{

        if err==io.EOF{

            fmt.Println("The connection is closed.")

            conn.Close()

        } else{

            fmt.Printf("Read Error:%s\n",err)

        }

        break

    }

    dataBuffer.Write(b[:n])



}



bufio 是 Buffered I/O缩写.



由于net.Conn类型实现了接口类型io.Reader中的Read接口,所以该接口的类型的一个实现类型。因此,我们可以使用bufio.NewReader函数来包装变量conn,像这样: 

reader:=bufio.NewReader(conn) 

可以调用reader变量的ReadBytes(“\n”)来接受一个byte类型的参数. 

当然很多情况下并不是查找一个但直接字符那么简单。比如,http协议中规定,消息头部的信息的末尾是两个空行,即是字符串”\r\n\r\n”, 

writer:=bufio.NewWriter(conn)来写入数据 

### close() 

关闭连接



### LocalAddr & RemoteAddr 方法 

conn.RmeoteAddr().Network() .String()



### SetDeadline, SetReadDeadline,SetWrteDeadline 



b:=make([]byte,10) 

conn.SetDeadline(time.Now().Add(2*time.Second)) 

for { 

n,err:=conn.Read(b) 

} 



我们通过调用time.Now()方法当前绝对时间加2s.2s后的那一刻.假设之后的第二次迭代时间超过2s.则第三次迭代尚未开始,就已经超时了,所以改为. 

“` 

b:=make([]byte,10) 

for { 

conn.SetDeadline(time.Now().Add(2*time.Second))



n,err:=conn.Read(b)

} 

“`



### 一个server 的简单实现 

image



缓冲器中的缓存机制,在很多时候,它会读取比足够多更多一点的数据到其中的缓冲器。会产生提前读取的问题.



net/http在 net/tcp的基础上构建了非常好用的接口,除此以外,标准库,net/rcp中api为我们提供了两个go程序之间建立通讯和交换数据的另外一种方式。 

远程过程调用(remote procedure call)也是基于TCP/IP协议栈的。 

Unix系统中,POSIX标准中定义的线程及其属性和操作方法被广泛认可. 

Linux中称为NPTL。



## 多线程



线程一个id, TID. 

编写高并发程序的建议: 

1. 控制临界区的纯度。临界区中的仅仅包含操作共享数据的代码。 

1. 控制临界区的粒度。 

1. 减少临界区中代码的执行耗时。 

1. 避免长时间的持有互斥变量。 

1. 优先使用院子操作而不是互斥量。



GO语言是操作系统提供的内核线程之上搭建了一个特有的两级线程模型。 

Goroutine代表的正确的含义: 

不要用共享内存的方式来通信。作为替代,应该用通信作为手段来共享内存。 

把数据放在共享内存区域中供多个线程中的程序访问的这种方式虽然在基本思想上非常简单,但是却让并发访问控制变得异常复杂。只有做好了很多约束和限制,才有可能让这些简单的方法正确的实施。但是正确性的同时,也需要有可伸缩性。 

Go语言不推荐用共享内存区的方式传递数据。作为替代,优先使用Channel。作为多个Goroutine之间的传递数据,并且还会保证其过程的同步。 

GO语言线程3个必须知道的核心元素。 

M:machine.一个M代表了一个内核线程。 

P:processor.一个P代表了M所需的上下文Content. 

G:Goroutine.一个G代表了对一段需要被并发执行的GO语言代码的封装。



GO并发编程.



type IntChan chan int. 

元素类型为int 的通道类型。 

这样的通道是双向类型的。



如果向一个nil(被关闭的channel)发送消息,则这个进程会被永久阻塞。 

需要注意的是:当我们向一个通道发送消息的时候,我们得到的是一个值的copy,当这个copy形成之后,之后对原来的值的修改都不会影响通道中的值。



select例子:



select语句和switch语句不同的是,跟在每个case 后面的只能是针对某个通道的发送语句或者接受语句。 

针对每个case 都有初始化了一个通道。通道的类型不受任何约束。元素的类型和容量是任意的。



分支选择规则



重做到右,从上到下。 

但是当系统发现第一个满足选择条件的case时,运行时系统就会执行该case所包含的语句。这也意味着其他case 会被忽略。如果同时有多个case满足条件,那么运行时,系统会通过一个伪随机算法决定那个case会被执行。例如下面的代码,发送5个范围在【1,3】的整数:



package main



import (

    "fmt"

)



func main() {

    chanCap := 5

    ch7 := make(chan int, chanCap)

    for i := 0; i < chanCap; i++ {

        select {

        case ch7 <- 1:

        case ch7 <- 2:

        case ch7 <- 3:

        }



    }

    for i := 0; i < chanCap; i++ {

        fmt.Printf("%v\n", <-ch7)

    }

}



但是,如果所有的case 都不满足(并且没有default case),就会阻塞住。直到某个case 被触发。 

所以通常情况下,default case 都是必要的.且default只能包含一个default case. 

两个变量赋值, e,ok:=<-chan 

第二个变量指的是通道是否被关闭。



package main



import (

    "fmt"

)



func main() {

    go func(){

        for {

            select{

            case e,ok:=<-ch11:

                if !ok{

                    fmt.Println("End.")

                    break

                }else{

                    fmt.Printf("%d\n",e)ß

                }



            }

        }

    }

}





//修改方案如下,不然会死锁。

func main(){

    go func(){

        var e int

        ok:=true   //声明在外,方便外层使用

        for {

            select{

            case e,ok=<-ch11:

                if !ok{

                    fmt.Println("End.")

                    break



                }else{

                    fmt.Printf("%d\n",e)

               }

            }

        if !ok{

            break

        }

        }

    }

}



有的时候我们需要早点关闭流程。这里我们添加新的超时行为。



timeout:=make(chan bool,1)

go func(){

    time.Sleep(time.Millisecond)

    timeout<-false

}



在原来的基础上,添加



go func(){

    var e int

    ok := true

    for {

       select{

       case e,ok=<-ch11:

        ...

        case ok=<-timeout:

        fmt.Println("Timeout.")

        break

       }

}

    if !ok{

     break

    }

}



非缓冲的Channel



图片



缓冲通道:由于元素的传递是异步的,所以发送操作在成功向通道发送元素值之后就会立即结束。 

非缓冲通道:等待能够接受该元素值的那个接收操作。并且只有确保成功接收,才会真正的完成执行。



package main



import (

    "fmt"

    "time"

)



func main() {

    unbufChan := make(chan int)

    go func() {

        fmt.Printf("Sleep a second ...\n")

        time.Sleep(time.Second)

        num := <-unbufChan

        fmt.Printf("Received a integer %d.\n", num)



    }()

    num := 1

    fmt.Printf("Send integer %d ...\n", num)

    unbufChan <- num

    fmt.Printf("Done")

}



select 语句与非缓冲通道



在使用select语句向某个非缓冲通道发送元素的时候,我们需要打起鸡血。因为,与操作缓冲通道的select语句相比,它被阻塞的概率非常之大。其基本原因依然是非缓冲通道会以同步的方式传递元素值。



time包与Channel



定时器



首先,结构体类型,time.Timer. new的两种方法:



time.NewTimer(Duration) & time.AfterFunc.



Duration=3*time.Hour+36*.time.Minute



Timer{

    Reset()

    Stop()

}

//到达时间,是通过字段C(chan time.Time)缓冲通道。 时间一道向自己发送一个time.Time类型元素。绝对到期时间。



重构之前的结构:



case <-time.NewTimer(time.Millisecond).C:

    fmt.Println("Timeout")

    ok=false

    break



定时器是可以被复用的。所以在case中的接受雨具中初始化是浪费的。下面的使用方式更好:



go func(){

   var timer *time.Timer

   for{

        select{

            case <-func()<-chan time.Time{

                if timer==nil{

                    timer=time.NewTimer(time.Millisecond)

                }else{

                    timer.Reset(time.Millisedcond)

                }

                return timer.C

            }():

            fmt.Println("Timeout")

            ok=false

            break

        }

   }

}



断续器



断续器与定时器的使用场景不同,当作超时触发器是不合适的。因为它对到期事件的传达虽然可能被放弃,当绝对不会被延误。断续器一旦被初始化,它所有的到期时间都是固定的了。 

固定不变的到期时间恰恰使断续器非常适合被作为定时任务的触发器。 

例如要求两次执行之间的最短间隔时间为10分钟。我们可以这样编写满足这一需求的代码:

高并发负载均衡器

QPS(Query Per Second,每秒查询量) & TPS(Transactions Per Second,每秒事物处理量)。

切片和数组都不是并发安全的。所以用一个chan来存储结果。

本文分享自微信公众号 - Golang语言社区(Golangweb)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2017-04-13

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Go 语言构建高并发分布式系统实践

    你知道互联网最抢手的技术人才有哪些吗?最新互联网职场生态报告显示,最抢手的十大互联网技术人才排名中Go语言开发人员位居第三,从中不难见得,Go语言的渗透率越来越...

    李海彬
  • Go代码打通HTTPs

    TL;DR 手工创建CA证书链,手写代码打通HTTPs的两端 HTTPs最近是一个重要的话题,同时也是一个有点难懂的话题。所以网上有大量的HTTPs/TLS/S...

    李海彬
  • Golang语言社区--LollipopGO开源项目搭建商城路由分发

    大家好,我是Golang社区主编彬哥,还是要继续社区的开源项目LollipopGO轻量级web框架实战商城。

    李海彬
  • 51. Socket服务端和客户端使用TCP协议通讯 | 厚土Go学习笔记

    Socket服务器是网络服务中常用的服务器。使用 go 语言实现这个业务场景是很容易的。 这样的网络通讯,需要一个服务端和至少一个客户端。 我们计划构建一个这样...

    李海彬
  • [基础篇]Go语言变量

    变量来源于数学,是计算机语言中能储存计算结果或能表示值抽象概念。变量可以通过变量名访问。 Go 语言变量名由字母、数字、下划线组成,其中首个字母不能为数字。 声...

    李海彬
  • Golang Template 简明笔记

    作者:人世间 链接:https://www.jianshu.com/p/05671bab2357 來源:简书 前后端分离的Restful架构大行其道,传统的模板...

    李海彬
  • 厚土Go学习笔记 | 29. 接口

    在go语言中,接口类型是由一组方法定义的集合。 一个类型是否实现了一个接口,就看这个类型是否实现了接口中定义的所有方法。在go语言中,无需特别的指明定义一个接口...

    李海彬
  • TOML-to-Go : 帮你快速生成 Go 结构体

    TOML 的目标是成为一个极简的配置文件格式。TOML 被设计成可以无歧义地被映射为哈希表,从而被多种语言解析。

    xuri
  • [转载]Golang 编译成 DLL 文件

    首先撰写 golang 程序 exportgo.go: package main import "C" import "fmt" //export Print...

    李海彬
  • [转载]Go JSON 技巧

    相对于很多的语言来说, Go 的 JSON 解析可谓简单至极. 问题 通常情况下, 我们在 Go 中经常这样进行 JSON 的解码: package main ...

    李海彬

扫码关注云+社区

领取腾讯云代金券