互斥体
在前面的例子中,我们看到了如何使用原子操作来管理简单的计数器状态。对于更复杂的状态,可以使用 互斥体 来安全地访问多个 goroutine 中的数据。
在这个例子中,状态(state)是一个映射。示例中的互斥将同步访问状态。我们将跟踪执行的读写操作数量。
这里启动 100 个 goroutine 来对状态执行重复读取,每个 goroutine 中每 ms 读取一次。对于每个读取,我们选择一个键来访问。Lock() 互斥体以确保对状态的独占访问,读取所选键的值, Unlock() 互斥体,并增加 readOps 计数。
我们还将启动 10 个 goroutine 来模拟写入,使用与读取相同的锁模式。
让 10 个 goroutine 在状态和互斥体上工作一秒钟。采集和报告最终操作计数。手机和报告最终操作计数。用最后的锁状态,显示它是如何结束了。
package main
import (
"sync"
"math/rand"
"sync/atomic"
"time"
"fmt"
)
func main(){
var state = make(map[int]int)
var mutex = &sync.Mutex{}
var readOps uint64 = 0
var writeOps uint64 = 0
for r := 0 ; r < 100 ; r ++{
go func() {
total := 0
for{
key:= rand.Intn(5)
mutex.Lock()
total += state[key]
mutex.Unlock()
atomic.AddUint64(&readOps , 1)
time.Sleep(time.Millisecond)
}
}()
}
for w:= 0 ; w < 10 ; w ++{
go func() {
for{
key := rand.Intn(5)
val := rand.Intn(100)
mutex.Lock()
state[key] = val
mutex.Unlock()
atomic.AddUint64(&writeOps,1)
time.Sleep(time.Millisecond)
}
}()
}
time.Sleep(time.Second)
readOpsFinal := atomic.LoadUint64(&readOps)
fmt.Println("readops:",readOpsFinal)
writeOpsFinal := atomic.LoadUint64(&writeOps)
fmt.Println("writeops",writeOpsFinal)
mutex.Lock()
fmt.Println("state",state)
mutex.Unlock()
}
Go排序实例
Go语言的 sort 包实现了内置和用户定义类型的排序。排序方法特定于内置类型,排序是一个就地排序,不会生成新的切片。
这里举例了一个 int 一个 string 类型的例子。
package main
import (
"sort"
"fmt"
)
func main(){
// example for string
strs := []string{"c","a","b"}
sort.Strings(strs)
fmt.Println("Strings:",strs)
//example of sorting
ints := []int{7,2,4}
sort.Ints(ints)
fmt.Println("Ints: ",ints)
//使用 sort 检查是相关 slice 是否已经被排好序
s := sort.IntsAreSorted(ints)
fmt.Println("sorted: ",s)
}
Go自定义函数
有时候,我们希望通过使用自然排序意外的其他方式对集合进行排序。例如,假设我们想通过字符串长度进行排序,下面是Go语言中自定义排序的类型。
为了使用Go语言中的自定义函数进行排序,我们需要一个相应的类型。这里创建了一个 ByLength 类型,它只是内置 []string 类型的别名。
需要实现 sort.Interface – Len , Less , Swap 在这个类型上,所以可以使用 sort 包中的一般 Sort 函数。 Len 和 Swap 通常在类型之间是相似的, Less 保存实际的自定义排序逻辑。在这个例子中,要按照字符串长度的增加顺序排序,因此在这里使用 len(s[i]) 和 len(s[j])。
所有这些都到位后,现在可以通过将原始 fruits 切片转换为 ByLength 来实现自定义排序,然后对该类型切片使用 sort.Sort() 方法。
package main
import (
"sort"
"fmt"
)
//定义类型
type ByLength []string
//重写下面几个接口函数
func (s ByLength) Len()int{
return len(s)
}
func (s ByLength) Swap(i,j int){
s[i],s[j] = s[j],s[i]
}
func (s ByLength) Less(i,j int) bool {
return len(s[i]) < len(s[j])
}
//正常调用方法
func main(){
fruits := []string{"peach","banana","kiwi"}
sort.Sort(ByLength(fruits))
fmt.Println(fruits)
}
Go panic 错误处理实例
Panic 通常意味着失去出乎意料的错了。大多数情况下,使用他来正常运行时不应该出现的错误,或者不准备妥善处理。
在程序中可使用 Panic 来检查意外的错误。
如果一个函数返回一个我们不知道如何(或想要)处理的错误值,那么 Panic 的常见用法就是终止。这里有一个例子,如果在创建一个新文件时需要意外的错误。
运行此程序将导致程序出错,打印错误消息和 goroutine 跟踪,并退出非零状态。
package main
import "os"
func main(){
panic("a problem")
_,err := os.Create("./test.log")
if err != nil{
panic(err)
}
}