Goland学习笔记
Xml文件的读取单条信息
1.先定义一个结构体用于存放读取的数据
type People struct {
XMLName xml.Name `xml:"people"`//整个元素节点
Id int `xml:"id,attr"`//一个是根节点一个是标签
Name string `xml:"name"`//结构体people的参数之一
Address string `xml:"address"`//结构体people的另一参数
}
2.然后调用相关库函数对xml文件进行读取,绑定数据并显示到控制台
peo:=new(Xml.People)
b,_:=ioutil.ReadFile("D://people.xml")
xml.Unmarshal(b,peo)
fmt.Println(peo)
多条信息
type Peoples struct {
XMLName xml.Name `xml:"peoples"`//整个元素节点
Version string `xml:"version,attr"`
Peos []People `xml:"people"`
}
peo:=new(Xml.Peoples)
b,_:=ioutil.ReadFile("D://people2.xml")
xml.Unmarshal(b,peo)
fmt.Println(peo)
Xml文件的生成
新建一个结构体并实例化,调用函数对信息生成xml文件
type People struct {
XMLName xml.Name `xml:"people"`//整个元素节点
Id int `xml:"id,attr"`//一个是根节点一个是标签
Name string `xml:"name"`//结构体people的参数之一
Address string `xml:"address"`//结构体people的另一参数
}
peo:=Xml.People{Id: 123,Name: "信息的结构体",Address: "D盘"}
//考虑xml文件的格式缩进
b,_:=xml.MarshalIndent(peo,""," ")
//追加头部信息
b = append([]byte(xml.Header),b...)
ioutil.WriteFile("D:/people3.xml",b,0777)
fmt.Println("执行结束")
日志
使用开发工具时,控制台打印的信息就是日志信息
项目最终发布后是没有开发工具的,而需要记录日志应该把信息输出到文件中,这个功能也是日志的功能
有三种级别日志输出
Print() 输出日志信息
Panic()打印日志信息,并触发panic,日志信息为Panic信息
Fatal()(打印日志信息后调用os.Exit(0)
所有日志信息打印时都带有时间,且颜色为红色
每种级别日志打印都提供了三个函数:Println(),Print(),Printf()
打印日志信息到文件/控制台
func LogPrint(path string) {
f,_:=os.OpenFile(path,os.O_APPEND|os.O_CREATE,0777)
logger:=log.New(f,"[Info]",log.Ltime)
logger.Println("打印日志信息")//打印到文件
log.Println("完成打印到文件")//打印到控制台
}
线程休眠是指main函数为主线程(协程),程序从上向下执行
可以用time包下的Sleep(n)来阻塞多少纳秒
延迟执行:
time.AfterFunc(3e9, func() {
fmt.Println("协程执行")
})
Goland从语言层面支持并发,在Goland中的goroutine(协程)类似其他语言的线程
几种主流并发模型
1.多线程,每个线程只处理一个请求,只有请求结束后对应的线程才会接收下一个请求。在高并发的情形下性能开销大。
2.基于回调的异步IO,在程序运行过程中可能产生大量的回调导致维护成本加大。
3.协程,不需要抢占式调用。
计数器,只要计数器有内容就会阻塞
方法:
Add(delta int)表示向内部计数器添加增量detal
Done()表示减少WaitGroup计数器的值,应该在程序最后执行
Wait()表示阻塞直到WaitGroup计数器为0
var wg sync.WaitGroup
wg.Add(5)
//五个协程
for i:=0;i<5;i++{
go func() {
fmt.Println("第",i,"次执行")
wg.Done()
}()
}
//当上面的协程执行完后这里的主协程才能继续执行下去
wg.Wait()
fmt.Println("主协程完成")
补充说明:
互斥锁
var (
num = 100
wg sync.WaitGroup//计数器
m sync.Mutex//锁
)
func demo(){
m.Lock()//先上锁,再执行
for i:0;i<10;i++{
num = num - 1
}
m.Unlock()//解锁
wg.Done()//计数器减一
}
func main() {
wg.Add(10)
for i:=0;i<10;i++{
go demo()
}
wg.Wait()
fmt.Println("程序结束")
}
读写锁:锁的是可执行的代码范围
在go语言中,map不是线程安全的,多个goroutine同时操作会出现错误
读锁可以有多个,写锁只能有一个,且读写锁不能同时存在
var rwm sync.RWMutex
var wg sync.WaitGroup
wg.Add(10)
m:=make(map[int]int)
for i:=0;i<10;i++ {
go func(j int) {
rwm.Lock()
m[j] = j
fmt.Println(m)
rwm.Unlock()
wg.Done()
}(i)
}
wg.Wait()
fmt.Println("完成")
channel:协程间的同步和通信
1.简单无缓存通道代码示例
此代码中如果没有从channel中取值c,d=<-ch语句,会阻塞,程序结束时go func并没有执行
下面代码示例演示了同步操作,类似与WaitGroup功能,保证程序结束时goroutinei已经执行完成
向goroutine中添加内容的代码会阻塞goroutine执行,所以要把ch<-1放入到goroutine有效代码最后一行。
无论是向channel存数据还是取数据都会阻塞
close(channel)关闭channel,关闭后只读不可写
注:定义的channel应该是放一个数据,然后跳转到取一个数据
//示例一:执行子协程,ch放入数据,再执行取出数据,然后主协程结束,类型之前WaitGroup的计数器功能,不过还新增一个传值功能用于通信
ch:=make(chan int)
go func() {
fmt.Println("执行")
ch <- 996//注意这句语句应该放到子协程的最后,发挥同步功能,确保子协程执行完再执行主协程
}()
a:=<-ch
fmt.Println(a)
//示例二:ch1用于两个子协程通信,ch2用于两个子协程与主协程的同步
ch1 := make(chan string)
ch2 := make(chan int)
go func() {
ch1 <- "这里是子协程1,传输数据开始"
ch2 <- 1
}()
go func() {
content := <- ch1//取数据
fmt.Println("取出子协程1的通道数据,这里是子协程2:",content)
ch2 <- 2
}()
<-ch2
<-ch2//注意要取两次,因为是存入两次
ch1 := make(chan string)
ch2 := make(chan int)
go func() {
for i:=97;i<=97+26;i++{
ch1 <- fmt.Sprintf("%c",i)
}
ch2 <- 1
}()
go func() {
for n:= range ch1{
fmt.Println(n)
}
}()
<-ch2
fmt.Println("主协程完成")
上述这段代码的理解是:ch1是用于两个子协程间进行同步的,ch2是用于两个子协程与主协程间进行同步的;
整个代码的运行流程应该是—>
首先进入上面的协程,将’a’写入通道ch1中(此时这个通道就只有等到读取出’a’后才可以继续写入),这个进程后面的代码就不执行先;
然后进入两个进程的竞争,上面的进程还是先抢到执行权,然后发现通道ch1写不进了,所以阻塞上面的进程,得找通道输出
之后才轮到下面的进程,用for range对通道ch1进行读取,然后执行for range里面的程序,如果是按 <-ch1 的方式读取,它不会执行后面的程序,会直接跳到通道输入那部分的程序(即另一个协程已经可以执行了),后面的过程按这三步类推。
直到最后一个值从通道中读取出来,然后上面的进程发现已经不用写ch1了,接着执行ch2的写入,最后ch2写出并结束程序。



