并发包含以下几种主流的实现模型
多进程,多进程是在操作系统层面进行并发的基本模式
多线程,多线程在大部分操作系统上都属于系统层面的并发模式
基于回调的非阻塞/异步IO,使用多线程模式会很快耗尽服务器的内存和CPU资源
协程,协程本质上是一种用户态线程,不需要操作系统来进行抢占式调度,且在真正的实现中寄存于线程中
声明
var channamechan elementtype
例 var ch chan int
var m map[string] chan bool map中的元素是bool类型的chan
定义
Ch :=make(chanint)
将一个数据写入到channel
Ch<-value
从channel读取数据
Value :=<-ch
单向channel声明:
Var ch1chan int ch1是一个正常的channel不是单向的
Var ch2chan <-float64 ch2是单向channel,只用于写float64的值
Var ch3<-chan int ch3是单向channel,只用于读int类型、
关闭channel: close(ch)
如何判断channel被关闭
X,ok:=<-ch 第二个值为false,则关闭
网络编程
无论我们期望使用什么类型的协议建立什么形式的链接,都只需调用net.Dail即可
Tcp
Conn,err:=net.Dail(“tcp”,”192.168.1.1:10086”)
Udp
Conn,err:=net.Dail(“udp”,”127.0.0.1:8080”)
ICMP
Conn,err:=net.Dial(“”ip4 :1”,1001.10.4.1;456””)
net.ResolveTCPAddr()用于解析地址和端口号
net.DialTcp()用于建立链接
http.Get()
要请求一个资源,只需调用http.Get()方法,示例代码如下:
Resp,err:=http.Get(“http://example.com/”)
Iferr!=nil{
//处理错误
return
}
deferresp.Body.close()
io.copy(os.stdout,resp.Body)
http函数,只需调用http.Post()方法依次传递下面的格式选要传递
http.PostForm()
posyfrom方法实现了标准编码格式、
http.Head
http中的head请求方式表明请求目标的URL信息
HTTP客户端
net/http包的client类型提供了如下几个方法,让我们可以用最简洁的方式实现HTTP请求
http.Get(),要请求一个资源,只需调用http.Get()方法
http.Get(“http://example.com/”)
http.Post(),要以POST方式发送数据,也很简单,只需要调用http.Post()方法并依次传递下面几个参数即可:
请求的目标URL
将要POST数据的资源类型(MIMEType)
数据的比特流([]byte流式)
http.Post(http://example.com/upload,”image/jpeg”,&imageDataBuf)
http.PostForm(),实现了标准编码格式为application/x-www-form-urlencoded的表单提交。
http.PostForm(http://example.com/posts,url.Value{“title”:{“article title”},”content”:{“article body”}})
http.Head() 只返回HTTPhead不返回HTTP body
http.Head(“http://example.com/”)
Net包对于I/O提供了便携式接口,包括TCP/IP/UDP,域名解析以及Unix Socket.尽管net包提供了大量访问底层的接口,但是大多数情况下,客户端仅仅只需要最基本的接口,例如Dail .Listen,Accepte以及分配的conn连接和listener接口。crypto/tls包使用相同的接口以及类似的Dail和listen函数。
funcInterfaceAddrs() 返回该系统的网络接口的地址列表
funcInterface() 返回该系统的网络接口列表
funcJoinHostPost() 将host和post合并为一个网络地址。一般格式为“host:post“;如果host含有冒号或百分号,格式为”[host]:port“
funcLookupCHAME(name string) 返回给定名字规范的DNS主机名称,如果调用者不关心name是否规范,可以直接调用LookupHost或者LookupIP,这两个函数都会在查询时考虑到name的规范性
funcLookupHost(hoststring) 通过利用本地解析器查找host进行查找,返回主机地址的数组
func LookupIP(hoststring) 通过利用本地解析器查找host,返回主机ipv4和ipv6地址的一个数组
packagemain
import (
"fmt"
"net"
)
funcmain(){
Addr ,_ :=net.InterfaceAddrs()
fmt.Println(Addr)
interfaces,_ :=net.Interfaces()
fmt.Println(interfaces)
hp :=net.JoinHostPort("127.0.0.1","8080")
fmt.Println(hp)
la ,_:=net.LookupAddr("127.0.0.1")
fmt.Println(la)
cname,_:=net.LookupCNAME("www.baidu.com")
fmt.Println(cname)
host,_:=net.LookupHost("www.baidu.com")
fmt.Println(host)
ip,_:=net.LookupIP("www.baidu.com")
fmt.Println(ip)
}
得出结果
[127.0.0.1/8192.168.247.128/24 ::1/128 fe80::20c:29ff:fe6d:c1c/64]
[{116436 lo up|loopback} {2 1500 eth000:0c:29:6d:0c:1c up|broadcast|multicast}]
127.0.0.1:8080
[localhostlocalhost.localdomain. localhost4 localhost4.localdomain4.]
www.a.shifen.com.
[14.215.177.3814.215.177.39]
[14.215.177.3914.215.177.38]
net 包中存在的error
func (e*AddrError) Error() string 错误
func (e*AddrError) Temporary() bool 该错误是否是一个临时错误
func (e*AddrError) Timeout() bool 该错误是否是超时错误
typeAddr 网络终端地址
typeAddr interface{
Network() string //网络名称
String() string //地址字符串表示
}
TypeConn //conn是一个通用的面向流的网络连接,多个goroutine可以调用conn中的方法
typeConn interface{
Read(b []byte)(n int,err error)
Read 从连接中读取数据,read的方法会在超过某个固定时间限制后返回一个表示超时的错误,该错误的Timeout()==True
Write (b[]byte)(n int,err error)
Write 从连接中读取数据,write的方法也会在超过某个固定时间限制后返回一个表示超时的错误,该错误的Timeout()==True
Close()error
Close方法关闭该连接,同时任何阻塞的read或write方法将不再阻塞,并返回错误
LocalAddr() Addr
返回本机网络地址
RemoteAddr() Addr
返回远端网络地址
}
funcDial(net,addr string) (Conn,error)
其中net是网络协议的名字,addr参数是IP地址或域名,而端口号以”:”的形式随在地址或域名的后面,端口号可选。如果连接成功,返回连接对象,否则返回error
Tcp 连接
conn,err :=net.Dial(“tcp”,”192.168.0.125:1231”)
udp 连接
conn,err :=net.Dial(“udp”,”192.168.0.125:1231”)
ICMP链接(使用协议名称)
conn,err :=net.Dial(“ip4:icmp”,”www.baidu.com”)
ICMP链接(使用协议编号)
conn,err :=net.Dial(“ip4:1”,”10.0.0.3”)
在成功建立连接后,我们就可以进行数据的发送和接收。发送数据时,使用conn的Write()成员方法,接收数据时使用Read()方法
Typeinterface interface表示一个在网络接口名和索引之间的映射,它也包含网络接口设备信息
funcInterfaceByIndex(index int) (*Interface, error) //通过指定的索引index返回相应的接口
funcInterfaceByName(name string) (*Interface, error) //通过指定的名字Name返回相应的接口
func(ifi *Interface) Addrs() ([]Addr, error) //返回指定接口的address
func(ifi *Interface) MulticastAddrs() ([]Addr, error) //MulticastAddrs返回网络接口ifi加入的多播组地址。
typeListener //Listern是一个用于面向流的网络协议的公用的网络监听器接口。多个线程可能会同时调用一个Listern的方法
typeListener interface{
Accept() (c Conn,err error) //等待并返回下一个连接到该连接的连接
Close() error //关闭listerner ,关闭后,任何阻塞accept的操作都不在阻塞,并且返回error
Addr() Addr //返回该接口的网络地址
}
funcListen(net, laddr string) (Listener, error) //返回在一个本地网络地址laddr上监听的Listener。网络类型参数net必须是面向流的网络:"tcp"、"tcp4"、"tcp6"、"unix"或"unixpacket"。具体参见Dial函数获取laddr的语法。
Go语言内建对JSON的支持。使用Go语言内置的encoding/json标准库,开发者可以轻松使用Go语言生成和解析JSON格式的数据
使用json.Marshal()函数可以对一组数据进行JSON格式的编码。
例如有如下的一个book实例
Type Bookstruct{
Title string
Author []string
Publisher string
IsPublished bool
Price float
}
Gobook:=Book{
“go语言编程”,
[“aaaa”,”ccc”],
“www.baidu.com”,
true,
9.99
}
可以使用json.Marshal()函数将gobook实例生成一段json格式的文本
b,err:=json.Marshal(gobook)
b==[]byte(`{
“Title”:“go语言编程”,
“Author”:[“aaaa”,”ccc”],
“Publisher”:“www.baidu.com”,
“Ispublished”:true,
“Price”:9.99
}`)
可以使用json.Unmarshal()函数将JSON格式的文本解码为Go预期的数据结构
var bookBook
err :=json.Unmarshal(b,&book)
解码后
book:=Book{
“go语言编程”,
[“aaaa”,”ccc”],
“www.baidu.com”,
true,
9.99
}
packagemain
import(
"io"
"log"
"net/http"
)
funchelloHandler(whttp.ResponseWriter,r*http.Request){
io.WriteString(w,"hello,world!")
}
funcmain(){
http.HandleFunc("/hello",helloHandler)
err:=http.ListenAndServe(":8080",nil)
iferr!=nil{
log.Fatal("ListenAndServe:",err.Error())
}
}
工程管理
Go 命令行工具
常用的功能:
goversion查看go的版本
go buildx.go 创建x的可执行文件
go fmtx.go 对x.go进行格式调整(调整成标准格式)
go runx.go直接执行x.go
进阶
反射:
基本概念:对所有接口进行反射,都可以得到一个包含Type和value的信息结构,type主要表达的是被反射的这个变量本身的类型信息,而value则为该变量实例本身的信息。
基本用法:
packagemain
import (
"fmt"
"reflect"
)
funcmain() {
var x float64 = 3.4
fmt.Println("type:",reflect.TypeOf(x))
v :=reflect.ValueOf(x)
fmt.Println("type:",v.Type())
fmt.Println("kind isfloat64:",v.Kind()==reflect.Float64)
fmt.Println("value",v.Float())
p :=reflect.ValueOf(&x)
fmt.Println("type ofp:",p.Type())
fmt.Println("settability ofp:",p.CanSet())
w :=p.Elem()
fmt.Println("settability ofw:",w.CanSet())
w.SetFloat(7.1)
fmt.Println(w.Interface())
fmt.Println(x)
}
打印出
type:float64
type: float64
kind isfloat64: true
value3.4
type ofp: *float64
settabilityof p: false
settabilityof w: true
7.1
7.1
package main
import"fmt"
//#include
//#include
import"C"
func Random() int{
returnint(C.random())
}
func Seed(i int){
C.srandom(C.uint(i))
}
func main(){
Seed(100)
fmt.Println("Random",Random())
}
事实上根本就不存一个名为C的包,这个import事实上只是一个信号,告诉Cgo需要工作了,然后对应这个import前面注释的C源代码自动生成包装性质的Go代码。上面的注释可以为块注释也可以是行注释。
对于C语言的原生类型,Cgo都会将其映射为go语言中的类型,C语言中的void* 指针类型在go语言中则用特殊的unsafe.Pointer类型来应对(但是需要导入import“unsafe”包)。
Cgo提供了一系列函数来提供支持:C.CStringC.GoString和C.GoStringN,为防止使用完未释放C.Cstring所产生的垃圾进而导致的内存泄露,需要使用defer语句
其实在import“C”的前面的注释块中,可以写任意的C程序,Cgo都会将其处理为合法的go代码
例如:
package main
/*
#include
Int add(int x,int y){
returnx+y;
}
*/
Import “C”
Import “fmt”
func main(){
fmt.Println(C.add(3,4))
}
输出结果为7
第一种写法:
//#cgo CFLAGS :-DPNG_DEBUG=1
//#cgo linux CFLAGS :-DLINUX=1
//#cgo LDFLAGS :lpng
//#include
Import “C”
使用CFLAGS来传入编译选项,使用LDFLAGS来传入连接选项。
第二种写法:
/#cgo pkg-config: png cairo
//#include
Import “C”
协程:轻量级线程
1. 能够在单一的系统线程中模拟多个任务的并发执行
2. 在一个特定的时间内,只有一个任务在执行,即并非真正的并发
3. 被动的任务调动方式,即任务没有主动抢占时间片的说法。当一个任务正在执行时,外部没有办法终止它。要进行任务切换,只能通过由该任务自身调用field()来主动让出CPU使用权
4. 每个协程都有自己的堆栈和局部变量
每个协程都包含三种状态:挂起,运行和停止停止通常表示该协程已经执行完成,挂起表示该协程尚未完成,但是让出了时间片
Blog:
学习笔记总3.19-3.23https://blog.csdn.net/shangguan_1234/article/details/79671079
学习笔记并发https://blog.csdn.net/shangguan_1234/article/details/79638930
学习笔记网络编程https://blog.csdn.net/shangguan_1234/article/details/79639040
实现多人聊天室https://blog.csdn.net/shangguan_1234/article/details/79640856
实现一个简单的网络爬虫https://blog.csdn.net/shangguan_1234/article/details/79667651