略
第一个go语言程序
package main
import "fmt"
func main() {
fmt.Println("Hello World")
}
go语言的变量定义
go语言完整的定义的变量的方法为 var 变量名 类型=值
,var name string ="fuwei"
,可以简写为name:="fuwei"
(这种只能在函数内使用,无法再包内使用),
package main
import "fmt"
func main() {
var a int=0
var b=0 //编译器自动猜测
c:=0
var d int
var e string
f:=""
arr:=[5]int{1,2,3,4,5}
var arr1 [5]int
var slice1 []int
slice2:=[]int{1,2,3,4,5}
fmt.Println(a,b,c,d,f,e,arr,arr1,slice1,slice2)
}
//简单的一个计算demo,勾股定理
func main() {
a, b := 3, 4
c := int(math.Sqrt(float64(a*a + b*b)))
fmt.Print(c)
}
常量的定义,和其他语言一样,常量的值是不可变动的,在不指定常量的类型的时候,编译器会自动转换
func main() {
a, b := 3, 4
c := int(math.Sqrt(float64(a*a + b*b)))
fmt.Print(c)
const e, f = 3, 4
g := math.Sqrt(float64(e*e + f*f))
fmt.Print(g)
}
枚举
在go语言中枚举的定义使用const
定义。可以直接定义
const (
Cpp = 0
java = 1
python = 2
)
使用iota表达式
package main
import "fmt"
const (
Cpp = iota //代表是自增
java
python
)
const (
b = 1 << (10 * iota) //代表使用1 << (10 * iota)来增加值
kb
mb
gb
tb
pb
)
func main() {
fmt.Println(Cpp, java, python)
fmt.Println(b, kb, mb, gb, tb, pb)
}
//结果:
0 1 2
1 1024 1048576 1073741824 1099511627776 1125899906842624
使用if
关键字,不用括号,可以跟多个语句
func main() {
const fileName = "/Users/fuwei/Documents/笔记/Go语言/01.goLang.md"
contents, err := ioutil.ReadFile(fileName)
if err!=nil {
fmt.Print(err)
} else {
fmt.Println(contents)
}
fmt.Println(grade(10))
}
if可以简写为
func main() {
const fileName = "/Users/fuwei/Documents/笔记/Go语言/01.goLang.md"
//contents, err := ioutil.ReadFile(fileName)
//这里的修改
if contents, err := ioutil.ReadFile(fileName)
err != nil {
fmt.Print(err)
} else {
fmt.Println(contents)
}
fmt.Println(grade(10))
}
swtich case,不用写break
编译器会自动添加
func grade(score int) string {
//默认
g := ""
switch {
case score < 60:
{
g = "F"
}
case score < 80:
{
g = "F"
}
case score < 100:
{
g = "A"
}
default:
{
panic("unknow")
}
}
return g
}
package main
import (
"bufio"
"fmt"
"os"
"strconv"
)
//07.循环
func main() {
fmt.Println(convertToBin(10),
convertToBin(15))
readFile("/Users/fuwei/Documents/笔记/Go语言/01.goLang.md")
}
func convertToBin(n int) string {
result := ""
for ; n > 0; n /= 2 {
lsb := n % 2
//转数据
result = strconv.Itoa(lsb) + result
}
return result
}
func readFile(filePath string) {
open, err := os.Open(filePath)
if err != nil {
panic(err)
}
scanner := bufio.NewScanner(open)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
}
_
来代替标准的函数
//多个返回值
func print(a int){
fmt.Println(a)
}
多个返回值
//多个返回值
func div(a, b int) (q, r int) {
i, _ := eval(a, b, "/")
return i, a % b
}
func apply(op func(float64, float64) float64, a, b float64) float64 {
//通过反射来
p := reflect.ValueOf(op).Pointer()
opName := runtime.FuncForPC(p).Name()
fmt.Println("Calling function is %s with args (%d,%d)", opName, a, b)
return op(a, b)
}
func sum(nums ...int) int {
sum := 0
for i := range nums {
sum = sum + nums[i]
fmt.Println(i)
}
return sum
}
值传递和引用传递
使用C++的代码来演示传值和传引用的区别
void pass_by_value(int a){
a++;
}
void pass_by_ref(int& a){
a++;
}
int main() {
void pass_by_value(int);
void pass_by_ref(int&);
int a=5;
pass_by_value(a);
std::cout<<"a:"<<a;
int b=5;
pass_by_ref(b);
std::cout<<"\n b:"<<b;
return 0;
}
打印结果是 a:5 b:6
go 语言只有值传递,没有引用传递,函数传递的时候都需要拷贝一份。
在GO语言中如何交换两个值?
package main
import "fmt"
func main() {
a, b := 3, 4
swap(a, b)
fmt.Println(a, b)
swap1(&a, &b)
fmt.Println(a, b)
}
func swap(a, b int) {
a, b = b, a
}
//能够交换
func swap1(a, b *int) {
*a, *b = *b, *a
}
数组是一个定长的数据集合:
数组的定义
var arr1 [5]int
arr2 := [3]int{1, 3, 5}
//让编译器判断
arr3 := [...]int{2, 4, 6, 8, 10}
多维数组
//多维数组
var grid [4][5]int
数组作为函数传递
//这样是拷贝整个数组,导致内存浪费
func printArr(arr [5]int) {
fmt.Println("print arr")
for i, v := range arr {
fmt.Println(i, v)
}
}
//这个是地址的拷贝
func printArrp(arr *[5]int) {
fmt.Println("print arr")
for i, v := range arr {
fmt.Println(i, v)
}
}
切片可以理解成动态的数组。
切片生成的对应的数组是原数组的一个视图,如果修改则会影响原来的数据
数组创建的时候,数据都是0。
package main
import "fmt"
//切片
func main() {
arr3 := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
fmt.Println("arr3[2:5]", arr3[2:5])
fmt.Println("arr3[:5]", arr3[:5])
fmt.Println("arr3[2:]", arr3[2:])
fmt.Println("arr3[:]", arr3[:])
//fmt.Println("arr3[2:5]",arr3[2:5])
s1 := arr3[2:6]
updateArr(s1)
fmt.Println("s1=", s1)
fmt.Println(arr3)
//slice再次建立slice
s2 := s1[3:7]
fmt.Println("s2=", s2)
//新的
s3:= append(s2, 10)
fmt.Println(s3)
fmt.Printf("arr=%v,len(arr)=%v cap(arr)=%v",arr,len(arr),cap(arr))
//超越cap
fmt.Println("\nover cap")
s4:= append(s3, 10,11,23,12,123)
fmt.Println(s4)
fmt.Printf("arr=%v,len(arr)=%v cap(arr)=%v",arr,len(arr),cap(arr))
}
func updateArr(s []int) {
s[0] = 100
}
package main
import "fmt"
//切片
func main() {
arr3 := [...]int{0,1,2,3,4,5,6,7,8,9}
fmt.Println("arr3[2:5]",arr3[2:5])
fmt.Println("arr3[:5]",arr3[:5])
fmt.Println("arr3[2:]",arr3[2:])
fmt.Println("arr3[:]",arr3[:])
//fmt.Println("arr3[2:5]",arr3[2:5])
}
切片生成的对应的数组是原数组的一个视图,如果修改则会影响原来的数据
s1 := arr3[2:6]
updateArr(s1)
fmt.Println("s1=", s1)
fmt.Println(arr3)
如果切片的子切片越界
,也就是slice的扩展,可以向后扩展
//slice再次建立slice,越界
s2 := s1[3:7]
fmt.Println("s2=", s2)
切片的长度和容量(len和cap)len是切片的长度,cap是向能向后扩展的长度
fmt.Printf("s1=%v,len(s1)=%v cap(s1)=%v",s1,len(s1),cap(s1))
s1=[100 3 4 5],len(s1)=4 cap(s1)=8
向切片添加数据,添加数据如果大于原来的数组Cap
,系统会重新分配更大的数组,原来的数组会被垃圾回收
//新的
s3:= append(s2, 10)
fmt.Println(s3)
fmt.Printf("arr=%v,len(arr)=%v cap(arr)=%v",arr,len(arr),cap(arr))
fmt.Println("\nover cap")
s4:= append(s3, 10,11,23,12,123)
fmt.Println(s4)
fmt.Printf("arr=%v,len(arr)=%v cap(arr)=%v",arr,len(arr),cap(arr))
Slice cap是2^n的增长
package main
import "fmt"
func main() {
var arr []int
for i := 0; i < 100; i++ {
arr = append(arr, 2*i+1)
fmt.Printf("arr=%v,len(arr)=%v cap(arr)=%v\n", arr, len(arr), cap(arr))
}
}
申请切片,指定长度和容量
长度代表需要使用的长度,容量代表真正的大小,如果大于这个容量会从新分配一个新的数组,这样保证了内存使用和内存的申请的一个平衡
//创建slice
s2:=make([]int ,16)
s3:=make([]int,10,32)//长度和容量
fmt.Printf("s2=%v,len(s2)=%v cap(s2)=%v\n", s2, len(s2), cap(s2))
fmt.Printf("s3=%v,len(s3)=%v cap(s3)=%v\n", s3, len(s3), cap(s3))
切片的拷贝,拷贝到dst的数组中。
copy(s2,arr)
fmt.Printf("s2=%v,len(s2)=%v cap(s2)=%v\n", s2, len(s2), cap(s2))
fmt.Printf("s3=%v,len(s3)=%v cap(s3)=%v\n", s3, len(s3), cap(s3))
切片的数据删除
//创建slice
s2 := make([]int, 16)
s3 := make([]int, 10, 32) //长度和容量
fmt.Printf("s2=%v,len(s2)=%v cap(s2)=%v\n", s2, len(s2), cap(s2))
fmt.Printf("s3=%v,len(s3)=%v cap(s3)=%v\n", s3, len(s3), cap(s3))
copy(s2, arr)
fmt.Printf("s2=%v,len(s2)=%v cap(s2)=%v\n", s2, len(s2), cap(s2))
fmt.Printf("s3=%v,len(s3)=%v cap(s3)=%v\n", s3, len(s3), cap(s3))
// 删除中间的元素
s2 = append(s2[:3], s2[4:]...)
fmt.Printf("s2=%v,len(s2)=%v cap(s2)=%v\n", s2, len(s2), cap(s2))
front := s2[0]
s2 = s2[1:]//删除头元素
tail := s2[len(s2)-1]
s2 = s2[:len(s2)-1]//删除尾元素
fmt.Printf("front:%v,tail:%v\n", front, tail)
fmt.Printf("s2=%v,len(s2)=%v cap(s2)=%v\n", s2, len(s2), cap(s2))
创建map,获取元素,判断可以是否存在,删除key,k-v遍历
package main
import "fmt"
func main(){
m:=map[string]string{
"key1":"val1",
"key2":"val2",
"key3":"val3",
"key4":"val4",
"key5":"val5",
}
m2:=make(map[string]int) //m2==empty
var m3 map[string] int //nil 可以参与安全运算
fmt.Println(m,m2,m3)
//map是无序的,每次顺序都不同
for k,v :=range m{
fmt.Printf("\n Key:%s,Value:%s\n",k,v)
}
fmt.Println(m["key1"])
fmt.Println(m["key10"])
//判断是否存在
if s,hasKey := m["key10"];hasKey{
fmt.Println(s)
}else {
fmt.Println("key not exist")
}
delete(m,"key1")
fmt.Println(m)
}
[实战]
寻找最长不含有除服的字符串的子串
eg. abcabcbb—-> abc , bbbbb—>b, pwwkew—->wke
package main
import "fmt"
func main() {
str := "abcdacbdacbd"
fmt.Println(norepeateSubstr(str))
}
func norepeateSubstr(s string) int {
start := 0
m := make(map[string]int)
max := 0
for i, ch := range []byte(s) {
if v, hasKey := m[string(ch)]; hasKey && v >= start {
start = m[string(ch)] + 1
}
//当前最大值减去起始值,比较大小
if i-start+1 > max {
max = i - start + 1
}
m[string(ch)] = i
}
return max
}
Eg. goLang处理中文,rune
go语言没有构造函数,如果想使用构造的方法,可以手写一个工厂函数。
package main
import "fmt"
type treeNode struct {
value int
left, right *treeNode
}
func main() {
var root treeNode
root = treeNode{value: 3}
root.left = &treeNode{}
root.right = &treeNode{}
root.right.left = new(treeNode)
root.left.right = createNode(2)
nodes := []treeNode{
{value: 3}, {}, {6, nil, &root},
}
root.left.right.setValue(4)
root.left.right.print()
root.left.right.setValuePoint(5)
root.left.right.print()
fmt.Println(nodes)
}
//类似构造函数
//此处返回的是局部变量的地址
func createNode(value int) *treeNode {
return &treeNode{value: value}
}
//代表方法成员方法
func (node treeNode) print() {
fmt.Println(node.value)
}
//这个方法是传值的,无法修改对象的值
func (node treeNode) setValue(value int) {
node.value = value
}
//这个方法是传值的,无法修改对象的值
func (node *treeNode) setValuePoint(value int) {
node.value = value
}
//使用是相同,但是是传值的
func print(node treeNode) {
fmt.Println(node.value)
}
定义类
type treeNode struct {
value int
left, right *treeNode
}
创建对象
var root treeNode
root = treeNode{value: 3}
root.left = &treeNode{}
root.right = &treeNode{}
root.right.left = new(treeNode)
root.left.right = createNode(2)
nodes := []treeNode{
{value: 3}, {}, {6, nil, &root},
}
创建构造函数
//类似构造函数
//此处返回的是局部变量的地址
func createNode(value int) *treeNode {
return &treeNode{value: value}
}
创建成员方法
//代表方法成员方法
func (node treeNode) print() {
fmt.Println(node.value)
}
//这个方法是传值的,无法修改对象的值
func (node treeNode) setValue(value int) {
node.value = value
}
func (node *treeNode) setValuePoint(value int) {
node.value = value
}
树的遍历
func(node *treeNode) traverse(){
if node==nil{
return
}
//此处不用判断nil 因为nil也可以使用方法
node.left.traverse()
node.print()
node.right.traverse()
}
public
,首字母小写:private
TreeNode的修改
目录结构:
├── main.go
├── tree
└── Node.go
代码如下:
package tree
import "fmt"
type Node struct {
Value int
Left, Right *Node
}
//类似构造函数
//此处返回的是局部变量的地址
func CreateNode(value int) *Node {
return &Node{Value: value}
}
//代表方法成员方法
func (node Node) Print() {
fmt.Println(node.Value)
}
//这个方法是传值的,无法修改对象的值
func (node Node)SetValue(value int) {
node.Value = value
}
func (node *Node) SetValuePoint(value int) {
node.Value = value
}
//使用是相同,但是是传值的
func print(node Node) {
fmt.Println(node.Value)
}
func(node *Node) Traverse(){
if node==nil{
return
}
node.Left.Traverse()
node.Print()
node.Right.Traverse()
}
main方法:
package main
import "fmt"
import "./tree"
func main() {
var root tree.Node
root = tree.Node{Value: 3}
root.Left = &tree.Node{}
root.Right = &tree.Node{}
root.Right.Left = new(tree.Node)
root.Left.Right = tree.CreateNode(2)
nodes := []tree.Node{
{Value: 3}, {}, {6, nil, &root},
}
root.Left.Right.SetValue(4)
root.Left.Right.Print()
//调用方式不发生改变,
//编译器会根据方法的参数来传递决定传地址还是值。
root.Left.Right.SetValuePoint(5)
root.Left.Right.Print()
fmt.Println(nodes)
root.Traverse()
}
package treeext
import "../tree"
type NodeEx struct {
Node *tree.Node
}
func (nodeEx *NodeEx) Traverse() {
if nodeEx == nil || nodeEx.Node == nil {
return
}
nodeEx.Node.Right.Traverse()
nodeEx.Node.Print()
nodeEx.Node.Left.Traverse()
}
package queue
type Queue []int
func (q *Queue) Push(value int) {
*q = append(*q, value)
}
func (q *Queue) Pop() int {
p := (*q)[0]
*q=(*q)[1:]
return p
}
go语言的接口比较灵活,不用显示的实现
接口的定义
type Retriever interface {
Get(url string) string
}
接口的实现 ,不需要显示的定义实现哪个接口
package retriever
type MockRetriever struct {
}
func (mockRetiever MockRetriever) Get(url string) string {
return "get baidu ok"
}
package retriever
import (
"net/http"
"net/http/httputil"
)
type HttpRetriever struct{}
func (retriever *HttpRetriever) Get(url string) string {
content, err := http.Get(url)
if err != nil {
panic(err)
}
defer content.Body.Close()
response, err := httputil.DumpResponse(content,true)
if err!=nil{
panic(err)
}
return string(response)
}
Defer 函数执行完成后释放,有panic
和return 也不受影响
package main
import (
"fmt"
)
func main() {
tryDefer()
}
func tryDefer() {
defer fmt.Println(1)
defer fmt.Println(2)
fmt.Println(3)
panic("error")
fmt.Println(4)
return
}
//打印结果;
3
2
1
错误处理
统一的错误处理
简单的文件服务器:
package main
import (
"io/ioutil"
"net/http"
"os"
)
type appHandler func(writer http.ResponseWriter, request *http.Request) error
func errWrapper(handler appHandler) func(http.ResponseWriter, *http.Request) {
return func(writer http.ResponseWriter, request *http.Request) {
err:=handler(writer,request)
if err!=nil{
switch {
case os.IsNotExist(err):
http.Error(writer,http.StatusText(http.StatusNotFound),http.StatusNotFound)
}
}
}
}
func main() {
http.HandleFunc("/List/", errWrapper(fileList))
err := http.ListenAndServe(":8888", nil)
if err != nil {
}
}
func fileList(writer http.ResponseWriter, request *http.Request) error {
path := request.URL.Path[len("/List/"):]
file, err := os.Open(path)
if err != nil {
//http.Error(writer, err.Error(), http.StatusInternalServerError)
return err
}
defer file.Close()
all, err := ioutil.ReadAll(file)
if err != nil {
panic(err)
}
writer.Write(all)
return err
}
相当于throw
package main
import "fmt"
func main() {
tryRecover()
}
func tryRecover() {
defer func() {
r:=recover()
if err,ok:=r.(error);ok{
fmt.Println("进入自定义错误处理:")
fmt.Println(err)
}else {
panic(r)
}
}()
b:=0
a:=5/b
fmt.Println(a)
}
学习到底32讲,做一个整理
package main
import (
"fmt"
"time"
)
func main() {
var arr [10]int
for i := 0; i < 10; i++ {
go func(i int) {
for {
arr[i]++
//主动交出控制权
//runtime.Gosched()
}
}(i)
}
time.Sleep(time.Second)
fmt.Println(arr)
}
go func(i int) {
for {
arr[i]++
//主动交出控制权
//runtime.Gosched()
}
}(i)
此处使用闭包的传值的方法
var c chan int
,c:=make(chan int
)
chan
没有接收的对象,则会造成死锁
chan
有缓存区,但是数量超过了缓存区,也会造成deadlock
package main
import (
"fmt"
)
func chandemo1() {
c := make(chan int)
c <- 1 //这里会阻塞,如果没有发出,就会造成死锁
c <- 2
n := <-c
n = <-c
fmt.Println(n)
}
//会报错
因为chan
的输入和输出的值的过程的阻塞的,如果没有发出,就会造成程序永久的阻塞,造成死锁问题.
为了解决这个问题,需要再开一个协程来处理数据chan
的发出
package main
import (
"fmt"
"time"
)
func chanDemo() {
c := make(chan int)
go func() {
for {
n := <-c
fmt.Println(n)
}
}()
c <- 1
c <- 2
time.Sleep(time.Millisecond)
}
func main() {
chanDemo()
}
package main
import (
"fmt"
"time"
)
func work(id int, c chan int) {
for {
fmt.Printf("work %d,do %c\n", id, <-c)
}
}
func chanDemo() {
var channels [10]chan int
for i := 0; i < 10; i++ {
channels[i] = make(chan int)
go work(i, channels[i])
}
for i := 0; i < 10; i++ {
channels[i] <- 'a' + i
}
for i := 0; i < 10; i++ {
channels[i] <- 'A' + i
}
time.Sleep(time.Millisecond)
}
func main() {
chanDemo()
}
chan int 作为返回值
package main
import (
"fmt"
"time"
)
func createWork(id int) chan int {
c := make(chan int)
go func() {
for {
fmt.Printf("work %d,do %c\n", id, <-c)
}
}()
return c
}
func chanDemo() {
var channels [10]chan int
for i := 0; i < 10; i++ {
channels[i] =createWork(i)
}
for i := 0; i < 10; i++ {
channels[i] <- 'a' + i
}
for i := 0; i < 10; i++ {
channels[i] <- 'A' + i
}
time.Sleep(time.Millisecond)
}
func main() {
chanDemo()
}
定义chan只能发数据或者收数据
chan <-
代表只能收数据和<-chant
外面只能获得数据,如果使用错误会导致编译失败
package main
import (
"fmt"
"time"
)
func createWork(id int) chan<- int {
c := make(chan int)
go func() {
for {
fmt.Printf("work %d,do %c\n", id, <-c)
}
}()
return c
}
func chanDemo() {
var channels [10]chan<- int
for i := 0; i < 10; i++ {
channels[i] =createWork(i)
}
for i := 0; i < 10; i++ {
channels[i] <- 'a' + i
}
for i := 0; i < 10; i++ {
channels[i] <- 'A' + i
}
time.Sleep(time.Millisecond)
}
func main() {
chanDemo()
}
chan增加缓冲区
协程虽然是轻量级的资源,但是发送完成后,立即进行资源的切换也会比较消耗资源,所以可以定义一个缓冲区,先收完数据再进行发送。
c := make(chan int, 3)//定义缓冲区长度为3
package main
import (
"fmt"
"time"
)
func work(id int,c chan int){
for {
fmt.Printf("work %d,do %c\n", id, <-c)
}
}
func bufferedChannel() {
c := make(chan int, 3)
go work(0,c)
c <- 'a'
c <- 'b'
c <- 'c'
c <- 'd'
time.Sleep(time.Millisecond)
}
func main() {
//chanDemo()
bufferedChannel()
}
如果知道数据已经发完了?
告知发送三个值后我已经发送完了,在接受方还要定义已经接受完的特殊值:
func finishSend() {
c := make(chan int, 3)
go work(0, c)
c <- 'a'
c <- 'b'
c <- 'c'
close(c)
time.Sleep(time.Millisecond)
}
func work(id int, c chan int) {
for {
i,ok:= <-c
if !ok{
//如果没有收到值,直接不接收数据了,因为此处是个死循环
break
}
fmt.Printf("work %d,do %c\n", id, i)
}
}
//或者使用rang函数
func work(id int, c chan int) {
for i := range c {
fmt.Printf("work %d,do %c\n", id, i)
}
}
43. 通过通信共享内存
package main
import (
"fmt"
)
type worker struct {
in chan int
done chan bool
}
func work(id int, c chan int, done chan bool) {
for i := range c {
fmt.Printf("work %d,do %c\n", id, i)
done <- true
}
}
func createWork(id int) worker {
w := worker{
in: make(chan int),
done: make(chan bool),
}
go work(id, w.in, w.done)
return w
}
func chanDemo() {
var workers [10]worker
for i := 0; i < 10; i++ {
workers[i] = createWork(i)
}
for i, worker := range workers {
worker.in <- 'a' + i
}
for i, worker := range workers {
worker.in <- 'A' + i
}
for _, worker := range workers {
<-worker.done
<-worker.done
}
//time.Sleep(time.Millisecond)
}
func main() {
chanDemo()
//bufferedChannel()
}
当打印完小写的时候会产生报错,报错的原因是work
的方法返回值没有接收。
sync.WaitGroup的使用,类似java中的CountDownLatch,可以动态追加
package main
import (
"fmt"
"sync"
)
type worker struct {
in chan int
wg *sync.WaitGroup
}
func work(id int, c chan int,wg *sync.WaitGroup) {
for i := range c {
fmt.Printf("work %d,do %c\n", id, i)
wg.Done()
}
}
func createWork(id int,wg *sync.WaitGroup) worker {
w := worker{
in: make(chan int),
}
go work(id, w.in,wg)
return w
}
func chanDemo() {
var wg sync.WaitGroup
var workers [10]worker
for i := 0; i < 10; i++ {
workers[i] = createWork(i,&wg)
}
for i, worker := range workers {
worker.in <- 'a' + i
wg.Add(1)
}
for i, worker := range workers {
worker.in <- 'A' + i
wg.Add(1)
}
wg.Wait()
//time.Sleep(time.Millisecond)
}
func main() {
chanDemo()
//bufferedChannel()
}
如果两个channel c1,c2,谁先获得值,就采用的谁的数据
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
var c1, c2 = generator(), generator()
var worker = createWork(0)
n := 0
var values []int
//产生一个channel 到时间会送入一个值
//此处使用10s
after := time.After(10 * time.Second)
tick := time.Tick(time.Second)
fmt.Println("开始时间:", time.Now())
for {
var activeWorker chan<- int
var activeValue int
if len(values) > 0 {
activeWorker = worker
activeValue = values[0]
}
select {
//缓存起来排队
case n = <-c1:
{
values = append(values, n)
}
case n = <-c2:
{
values = append(values, n)
}
//负责打印
case activeWorker <- activeValue:
{
values = values[1:]
}
case <-time.After(800 * time.Millisecond):
{
fmt.Println("timeout")
}
case <-tick:
{
fmt.Println("queue len:", len(values))
}
//到时间退出
case <-after:
{
fmt.Println("到时间结束:", time.Now())
return
}
//非阻塞
default:
{
}
}
}
}
func work(id int, c chan int) {
for i := range c {
time.Sleep(time.Second)
fmt.Printf("work %d,do %d\n", id, i)
}
}
func createWork(id int) chan<- int {
c := make(chan int)
go work(id, c)
return c
}
func generator() chan int {
out := make(chan int)
go func() {
i := 0
for {
fmt.Println("生成数字", i)
time.Sleep(time.Duration(rand.Intn(1500)) * time.Millisecond)
out <- i
i++
}
}()
return out
}
package main
import (
"fmt"
"time"
)
type atomicInt int
func (a *atomicInt) increment() {
*a++
}
func (a *atomicInt) get() int {
return int(*a)
}
func main() {
var a atomicInt
a.increment()
go func() {
a.increment()
}()
time.Sleep(time.Second)
fmt.Println(a)
}
使用go run -race main.go
查看执行的轨迹
==================
WARNING: DATA RACE
Read at 0x00c000134008 by main goroutine:
main.main()
/Users/fuwei/work/goPros/go/learnGo/bytedance/ch46.mutex/main.go:25 +0xc5
Previous write at 0x00c000134008 by goroutine 7:
main.(*atomicInt).increment()
/Users/fuwei/work/goPros/go/learnGo/bytedance/ch46.mutex/main.go:11 +0x51
main.main.func1()
/Users/fuwei/work/goPros/go/learnGo/bytedance/ch46.mutex/main.go:22 +0x32
Goroutine 7 (finished) created at:
main.main()
/Users/fuwei/work/goPros/go/learnGo/bytedance/ch46.mutex/main.go:21 +0xaa
==================
2
Found 1 data race(s)
exit status 66
增加sync.Mutex
对操作加锁
package main
import (
"fmt"
"sync"
"time"
)
type atomicInt struct {
value int
lock sync.Mutex
}
func (a *atomicInt) increment() {
a.lock.Lock()
defer a.lock.Unlock()
a.value++
}
func (a *atomicInt) get() int {
a.lock.Lock()
defer a.lock.Unlock()
return a.value
}
func main() {
var a atomicInt
a.increment()
go func() {
a.increment()
}()
time.Sleep(time.Second)
fmt.Println(a.get())
}
文件服务器增加pprof
package main
import (
"fmt"
"io/ioutil"
"net/http"
//避免没有的时候 报错
_ "net/http/pprof"
"os"
)
type appHandler func(writer http.ResponseWriter, request *http.Request) error
func errWrapper(handler appHandler) func(http.ResponseWriter, *http.Request) {
return func(writer http.ResponseWriter, request *http.Request) {
err := handler(writer, request)
if err != nil {
switch {
case os.IsNotExist(err):
http.Error(writer, http.StatusText(http.StatusNotFound), http.StatusNotFound)
}
}
}
}
func main() {
http.HandleFunc("/file/", errWrapper(fileList))
http.HandleFunc("/List/", errWrapper(getDirList))
err := http.ListenAndServe(":8888", nil)
if err != nil {
}
}
func getDirList(writer http.ResponseWriter, request *http.Request) error {
dir_list, e := ioutil.ReadDir("./")
if e != nil {
fmt.Println("read dir error")
return e
}
for _, v := range dir_list {
bytes := []byte(v.Name() + "\n")
writer.Write(bytes)
}
return e
}
func fileList(writer http.ResponseWriter, request *http.Request) error {
path := request.URL.Path[len("/List/"):]
file, err := os.Open(path)
if err != nil {
//http.Error(writer, err.Error(), http.StatusInternalServerError)
return err
}
defer file.Close()
all, err := ioutil.ReadAll(file)
if err != nil {
panic(err)
}
writer.Write(all)
return err
}
访问localhost:8888/debug/pprof
,显示当前的程序运行的堆栈:
go tool pprof http://localhost:8888/debug/pprof/profile
使用这个命令获得30s的cpu的运行情况
碰到的故障:failed to execute dot. Is Graphviz installed? Error: exec: "dot": executable file not found in $PATH
是电脑没有安装gvedit导致
查看文档godoc -http :8888
更新中…