go语言学习第五天==>并发编程,go关键词,并发通信channel、select关键字、缓冲机制、socket编程
- 并 发 编 程
goroutine是Go语言中的轻量级线程实现
go关键词
在一个函调用前加上go关键字,
这次调用就会在一个新的goroutine中并发执行,当函数返回则结束,如果这个函数有返回值则会被丢弃
demo
当mian函数结束时程序就退出,即终止所有goroutine
func Add(x, y int) {
z := x + y fmt.Println(z)
}
go Add(1, 1) //关键行
///
并发通信channel
channel 用于线程之间的通信,与普通变量的不同之处在于
向channel写入/读出数据的时候线程是阻塞的,直到操作完成
起到线程通信安全的作用,
其他语言是共享内存,channel是线程通信
声明形式:var 变量名 chan 变量类型
var ch chan int
var m map[string] chan bool
定义channel,直接使用make()即可
ch := make(chan int)
将数据写入(发送至)channel语法↓
ch <- value
从channel中读出数据语法↓
value := <-ch
select关键字
用于处理异步IO问题
语法:↓(类似switch语句但是和switch有很大区别)
select {
case <-chan1: // 如果chan1成功读到数据,则进行该case处理语句
case chan2 <- 1: // 如果成功向chan2写入数据,则进行该case处理语句
default: // 如果上面都没有成功,则进入default处理流程
}
1 select后面不带判断条件,直接查看case
2 每个case语句后必须是channel的操作
3 有一个case执行成功则结束
4 从上到下执行case直到有一个成功的
5 如果case中的操作都没有成功则到default
缓冲机制:当需要持续传输大量数据时使用
创建使用make() 在第二个参数指定缓冲区大小↓
即使没有读取方,西二方也可以一直往channel里面写入
缓冲区用完之前都不会被阻塞
c := make(chan int, 1024)
**缓冲读取:**与读取非缓冲区一致,但是也可以用range全部读取
for i := range c {
fmt.Println("Received:", i)
}
//
超时机制
向channel西二数据时发现channel已满,
或者读取时发现channel是空的
如果不正确处理这些情况,有可能会锁死
处理方法用上面的select关键字
demo
// 首先,我们实现并执行一个匿名的超时等待函数 timeout := make(chan bool, 1) go func() {
time.Sleep(1e9) // 等待1秒钟
timeout <- true
}()
// 然后我们把timeout这个channel利用起来
select {
case <-ch: // 从ch中读取到数据
case <-timeout:
// 一直没有从ch中读取到数据,但从timeout中读取到了数据
}
这样使用select机制可以避免永久等待问题↑
channel是原生类型不仅支持传递还支持类型转换
///
单向channel:即只允许读或者写
声明:
var ch1 chan int // ch1是一个正常的channel,不是单向的
var ch2 chan<- float64// ch2是单向channel,只用于写float64数据
var ch3 <-chan int // ch3是单向channel,只用于读取int数据
初始化
ch4 := make(chan int)
//使用4初始化单向5
ch5 := <-chan int(ch4) // ch5就是一个单向的读取channel
//使用4初始化单向6
ch6 := chan<- int(ch4) // ch6 是一个单向的写入channel
关闭channel直接使用内置的close()函数即可
close(ch)
现在go语言默认只使用一个cpu,要想使用多个CPU可以这样
runtime.GOMAXPROCS(16)
获取CPU数量的函数
NumCPU()
/
Socket编程
以前网络编程的步骤:
(1) 建立Socket:使用socket()函数。
(2) 绑定Socket:使用bind()函数。
(3) 监听:使用listen()函数。或者连接:使用connect()函数。 (4) 接受连接:使用accept()函数。
(5) 接收:使用receive()函数。或者发送:使用send()函数。
但是go对此过程进行了抽象和封装,无论建立什么都只需要用
:net.Dial()即可。
Dial()函数
原型:
func Dial(net, addr string) (Conn, error)
net是网络协议名称
addr是IP地址或者域名,端口号以:形式追加
成功则返回连接对象,失败则返回错误
demo:
tcp:
conn, err := net.Dial("tcp", "192.168.0.10:2100")
udp:
conn, err := net.Dial("udp", "192.168.0.12:975")
ICMP:
conn, err := net.Dial("ip4:icmp", "www.baidu.com")
ICMP使用协议编号
conn, err := net.Dial("ip4:1", "10.0.0.3")
tcp demo
package main
import ( "net" "os" "bytes" "fmt" )
func main() {
if len(os.Args) != 2 {
fmt.Fprintf(os.Stderr, "Usage: %s host:port", os.Args[0]) os.Exit(1)
}
service := os.Args[1]
conn, err := net.Dial("tcp", service)
checkError(err)
_, err = conn.Write([]byte("HEAD / HTTP/1.0\r\n\r\n"))
checkError(err)
result, err := readFully(conn)
checkError(err)
fmt.Println(string(result))
os.Exit(0)
}
func checkError(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
os.Exit(1)
}
}
func readFully(conn net.Conn) ([]byte, error) {
defer conn.Close()
result := bytes.NewBuffer(nil) var buf [512]byte for {
n, err := conn.Read(buf[0:])
result.Write(buf[0:n])
if err != nil {
if err == io.EOF {
break
}
return nil, err
}
}
return result.Bytes(), nil }