Go内部培训——节点解析61-63

61. Go 信号处理

  • 有的时候我们希望Go能够智能地处理Unix信号。例如我们希望一个server接收到一个SIGTERM的信号
    时,能够自动地停止;或者一个命令行工具接收到一个SIGINT信号时,能够停止接收输入。现在我们来看
    下如何使用channel来处理信号。
package main
import "fmt"
import "os"
import "os/signal"
import "syscall"
func main() {
// Go信号通知通过向一个channel发送``os.Signal`来实现。
// 我们将创建一个channel来接受这些通知,同时我们还用
// 一个channel来在程序可以退出的时候通知我们
sigs := make(chan os.Signal, 1)
done := make(chan bool, 1)
// `signal.Notify`在给定的channel上面注册该channel
// 可以接受的信号
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
// 这个goroutine阻塞等待信号的到来,当信号到来的时候,
// 输出该信号,然后通知程序可以结束了
go func() {
sig := <-sigs
fmt.Println()
fmt.Println(sig)
done <- true
}()
// 程序将等待接受信号,然后退出
fmt.Println("awaiting signal")
<-done
fmt.Println("exiting")
}

62. Go 原子计数器

  • Go里面的管理协程状态的主要机制就是通道通讯。这些我们上面的例子介绍过。这里还有一些管理状态的
    机制,下面我们看看多协程原子访问计数器的例子,这个功能是由sync/atomic包提供的函数来实现的。
package main
import "fmt"
import "time"
import "sync/atomic"
import "runtime"
func main() {
// 我们使用一个无符号整型来代表一个永远为正整数的counter
var ops uint64 = 0
// 为了模拟并行更新,我们使用50个协程来每隔1毫秒来
// 增加一下counter值,注意这里的50协程里面的for循环,
// 也就是说如果主协程不退出,这些协程将永远运行下去
// 所以这个程序每次输出的值有可能不一样
for i := 0; i < 50; i++ {
go func() {
for {
// 为了能够保证counter值增加的原子性,我们使用
// atomic包中的AddUint64方法,将counter的地址和
// 需要增加的值传递给函数即可
atomic.AddUint64(&ops, 1)
// 允许其他的协程来处理
runtime.Gosched()
}
}()
}
//等待1秒中,让协程有时间运行一段时间
time.Sleep(time.Second)
// 为了能够在counter仍被其他协程更新值的同时安全访问counter值,
// 我们获取一个当前counter值的拷贝,这里就是opsFinal,需要把
// ops的地址传递给函数`LoadUint64`
opsFinal := atomic.LoadUint64(&ops)
fmt.Println("ops:", opsFinal)
}

63. Go 正则表达式

  • Go内置了对正则表达式的支持,这里是一般的正则表达式常规用法的例子。
package main
import "bytes"
import "fmt"
import "regexp"
func main() {
// 测试模式是否匹配字符串,括号里面的意思是
// 至少有一个a-z之间的字符存在
match, _ := regexp.MatchString("p([a-z]+)ch", "peach")
fmt.Println(match)
// 上面我们直接使用了字符串匹配的正则表达式,
// 但是对于其他的正则匹配任务,你需要使用
// `Compile`来使用一个优化过的正则对象
r, _ := regexp.Compile("p([a-z]+)ch")
// 正则结构体对象有很多方法可以使用,比如上面的例子
// 也可以像下面这么写
fmt.Println(r.MatchString("peach"))
// 这个方法检测字符串参数是否存在正则所约束的匹配
fmt.Println(r.FindString("peach punch"))
// 这个方法查找第一次匹配的索引,并返回匹配字符串
// 的起始索引和结束索引,而不是匹配的字符串
fmt.Println(r.FindStringIndex("peach punch"))
// 这个方法返回全局匹配的字符串和局部匹配的字符,比如
// 这里会返回匹配`p([a-z]+)ch`的字符串
// 和匹配`([a-z]+)`的字符串
fmt.Println(r.FindStringSubmatch("peach punch"))
// 和上面的方法一样,不同的是返回全局匹配和局部匹配的
// 起始索引和结束索引
fmt.Println(r.FindStringSubmatchIndex("peach punch"))
// 这个方法返回所有正则匹配的字符,不仅仅是第一个
fmt.Println(r.FindAllString("peach punch pinch", -1))
// 这个方法返回所有全局匹配和局部匹配的字符串起始索引
// 和结束索引
fmt.Println(r.FindAllStringSubmatchIndex("peach punch pinch", -1))
// 为这个方法提供一个正整数参数来限制匹配数量
fmt.Println(r.FindAllString("peach punch pinch", 2))
 
//上面我们都是用了诸如`MatchString`这样的方法,其实
// 我们也可以使用`[]byte`作为参数,并且使用`Match`
// 这样的方法名
fmt.Println(r.Match([]byte("peach")))
// 当使用正则表达式来创建常量的时候,你可以使用`MustCompile`
// 因为`Compile`返回两个值
r = regexp.MustCompile("p([a-z]+)ch")
fmt.Println(r)
// regexp包也可以用来将字符串的一部分替换为其他的值
fmt.Println(r.ReplaceAllString("a peach", "<fruit>"))
// `Func`变量可以让你将所有匹配的字符串都经过该函数处理
// 转变为所需要的值
in := []byte("a peach")
out := r.ReplaceAllFunc(in, bytes.ToUpper)
fmt.Println(string(out))
}

64. Go 指针

  • Go支持指针,可以用来给函数传递变量的引用。
package main
import "fmt"
// 我们用两个不同的例子来演示指针的用法
// zeroval函数有一个int类型参数,这个时候传递给函数的是变量的值
func zeroval(ival int) {
ival = 0
}
// zeroptr函数的参数是int类型指针,这个时候传递给函数的是变量的地址
// 在函数内部对这个地址所指向的变量的任何修改都会反映到原来的变量上。
func zeroptr(iptr *int) {
*iptr = 0
}
func main() {
i := 1
fmt.Println("initial:", i)
zeroval(i)
fmt.Println("zeroval:", i)
// &操作符用来取得i变量的地址
zeroptr(&i)
fmt.Println("zeroptr:", i)
// 指针类型也可以输出
fmt.Println("pointer:", &i)
}

猜你喜欢

转载自blog.csdn.net/boss2967/article/details/85121684
今日推荐