From 2e1f26ee02d5feb535b49d9c92719c7677afdc2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=B9=E5=B0=86=E5=B0=86?= Date: Mon, 9 Dec 2019 20:24:53 +0800 Subject: [PATCH 1/2] =?UTF-8?q?add:3&&4=E5=91=A8=E4=BD=9C=E4=B8=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../caojiangjiang/week3/leetcode/leetCode.go | 1 + .../caojiangjiang/week3/practice/question1.go | 52 +++++++ .../caojiangjiang/week3/practice/question2.go | 135 ++++++++++++++++++ homework/caojiangjiang/week3/theory/theory.md | 0 .../caojiangjiang/week4/letCode/leetcode.go | 1 + homework/caojiangjiang/week4/theory/throry.md | 0 .../caojiangjiang/week4/workPool/workPool.go | 93 ++++++++++++ 7 files changed, 282 insertions(+) create mode 100644 homework/caojiangjiang/week3/leetcode/leetCode.go create mode 100644 homework/caojiangjiang/week3/practice/question1.go create mode 100644 homework/caojiangjiang/week3/practice/question2.go create mode 100644 homework/caojiangjiang/week3/theory/theory.md create mode 100644 homework/caojiangjiang/week4/letCode/leetcode.go create mode 100644 homework/caojiangjiang/week4/theory/throry.md create mode 100644 homework/caojiangjiang/week4/workPool/workPool.go diff --git a/homework/caojiangjiang/week3/leetcode/leetCode.go b/homework/caojiangjiang/week3/leetcode/leetCode.go new file mode 100644 index 0000000..fb70a9a --- /dev/null +++ b/homework/caojiangjiang/week3/leetcode/leetCode.go @@ -0,0 +1 @@ +package leetcode diff --git a/homework/caojiangjiang/week3/practice/question1.go b/homework/caojiangjiang/week3/practice/question1.go new file mode 100644 index 0000000..58ed3c1 --- /dev/null +++ b/homework/caojiangjiang/week3/practice/question1.go @@ -0,0 +1,52 @@ +package main + +import ( + "encoding/json" + "fmt" + "net/http" + "log" + "os" + "strings" + "time" +) + +func sayhello(w http.ResponseWriter, r *http.Request) { + go writeLog(w, r); + fmt.Fprintf(w, "Hello world!") // 这个写入到 w 的是输出到客户端的 +} + +/** + * 方法功能:监听端口,异步写入日志 + */ +func main() { + http.HandleFunc("/", sayhello) // 设置访问的路由 + err := http.ListenAndServe(":9090", nil) // 设置监听的端口 + if err != nil { + log.Fatal("ListenAndServe: ", err) + } +} + +func writeLog(w http.ResponseWriter, r *http.Request) { + url := r.URL.Path + query := r.URL.RawQuery + method := r.Method + methodInfoMap := map[string] interface{}{ + "url" : url, + "method" : method, + "query" : query, + "response": "hello world!", + } + methodInfoJson, _ := json.Marshal(methodInfoMap) + methodInfoStr := string(methodInfoJson) + fileName := "/tmp/homework.log" // 根目录下的tmp目录下 + _, err := os.Stat(fileName) + if err != nil || os.IsNotExist(err) { + os.Create(fileName) + } + fd,_:=os.OpenFile(fileName,os.O_RDWR|os.O_CREATE|os.O_APPEND,0666) + defer fd.Close() + fd_time:=time.Now().Format("2006-01-02 15:04:05"); + fd_content:=strings.Join([]string{"======",fd_time,"=====",methodInfoStr,"\n"},"") + buf:=[]byte(fd_content) + fd.Write(buf) +} \ No newline at end of file diff --git a/homework/caojiangjiang/week3/practice/question2.go b/homework/caojiangjiang/week3/practice/question2.go new file mode 100644 index 0000000..289934b --- /dev/null +++ b/homework/caojiangjiang/week3/practice/question2.go @@ -0,0 +1,135 @@ +package main + +/** + * 方法功能:平滑重启 + */ +import ( + "context" + "errors" + "flag" + "log" + "net" + "net/http" + "os" + "os/exec" + "os/signal" + "syscall" + "time" +) + +var ( + server *http.Server + listener net.Listener + // 在 go 标准库中提供了一个包:flag,方便进行命令行解析 + // flag.Xxx(),其中 Xxx 可以是 Int、String,Bool 等;返回一个相应类型的指针 + graceful = flag.Bool("graceful", false, "listen on fd open 3 (internal use only)") +) + +func sleep(w http.ResponseWriter, r *http.Request) { + // 请输入(x)s,比如:10s,不要忘记加s + duration, err := time.ParseDuration(r.FormValue("duration")) + if err != nil { + http.Error(w, err.Error(), 400) + return + } + time.Sleep(duration) + w.Write([]byte("Hello World")) +} + + +// 父进程监听重启信号 +// 在收到重启信号后,父进程调用 fork ,同时传递 socket 描述符给子进程 +// 子进程接收并监听父进程传递的 socket 描述符 +// 在子进程启动成功之后,父进程停止接收新连接,同时等待旧连接处理完成(或超时) +// 父进程退出,热重启完成 + +// 执行步骤: +// 1. 启动这个程序(你要是不知道咋启动,劝你还是别看这代码了,看了你也看不懂,懂了你也不会用) +// 2. 浏览器运行localhost:5007/sleep +// 3. 打开iterm 输入 kill -USR2 进程号(用ps -ef|grep question就可以搜到了) +// 4. 观察输出 +func main() { + // 解析命令行参数到定义的flag + flag.Parse() + + http.HandleFunc("/sleep", sleep) + server = &http.Server{Addr: ":5007"} + + var err error + if *graceful { + log.Print("main: Listening to existing file descriptor 3.") + // cmd.ExtraFiles: If non-nil, entry i becomes file descriptor 3+i. + // when we put socket FD at the first entry, it will always be 3(0+3) + // 子进程的 0, 1, 2 是预留给标准输入、标准输出、错误输出,故传递的 socket 描述符应放在子进程的 3 + f := os.NewFile(3, "") + listener, err = net.FileListener(f) + } else { + log.Print("main: Listening on a new file descriptor.") + listener, err = net.Listen("tcp", server.Addr) + } + + if err != nil { + log.Fatalf("listener error: %v", err) + } + + go func() { + // server.Shutdown() stops Serve() immediately, thus server.Serve() should not be in main goroutine + err = server.Serve(listener) + log.Printf("server.Serve err: %v\n", err) + }() + signalHandler() + log.Printf("signal end") +} + +func reload() error { + tl, ok := listener.(*net.TCPListener) + if !ok { + return errors.New("listener is not tcp listener") + } + + f, err := tl.File() + if err != nil { + return err + } + // 设置传递给子进程的参数(包含 socket 描述符) + args := []string{"-graceful=true"} + cmd := exec.Command(os.Args[0], args...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + // put socket FD at the first entry + cmd.ExtraFiles = []*os.File{f} + log.Println(cmd) + return cmd.Start() +} + +func signalHandler() { + ch := make(chan os.Signal, 1) + // 注册一个os.Signal以监听 + signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM, syscall.SIGUSR2) + for { + sig := <-ch + log.Printf("signal: %v", sig) + + // timeout context for shutdown + ctx, _ := context.WithTimeout(context.Background(), 100*time.Second) + switch sig { + case syscall.SIGINT, syscall.SIGTERM: + // stop + log.Printf("stop") + signal.Stop(ch) + server.Shutdown(ctx) + log.Printf("graceful shutdown") + return + case syscall.SIGUSR2: + // reload + log.Printf("reload") + err := reload() + if err != nil { + log.Fatalf("graceful restart error: %v", err) + } + server.Shutdown(ctx) + log.Printf("graceful reload") + return + } + } +} \ No newline at end of file diff --git a/homework/caojiangjiang/week3/theory/theory.md b/homework/caojiangjiang/week3/theory/theory.md new file mode 100644 index 0000000..e69de29 diff --git a/homework/caojiangjiang/week4/letCode/leetcode.go b/homework/caojiangjiang/week4/letCode/leetcode.go new file mode 100644 index 0000000..eaa93c4 --- /dev/null +++ b/homework/caojiangjiang/week4/letCode/leetcode.go @@ -0,0 +1 @@ +package letCode diff --git a/homework/caojiangjiang/week4/theory/throry.md b/homework/caojiangjiang/week4/theory/throry.md new file mode 100644 index 0000000..e69de29 diff --git a/homework/caojiangjiang/week4/workPool/workPool.go b/homework/caojiangjiang/week4/workPool/workPool.go new file mode 100644 index 0000000..2d630ae --- /dev/null +++ b/homework/caojiangjiang/week4/workPool/workPool.go @@ -0,0 +1,93 @@ +package main + +import ( + "fmt" + "math/rand" + "sync" + "time" +) + +// 创建一个工作池 +type Task struct { + ID int + randnum int +} + +type Result struct { + task Task + result int +} + +var tasks = make(chan Task, 10) +var results = make(chan Result, 10) + +func process(num int) int { + sum := 0 + for num != 0 { + digit := num % 10 + sum += digit + num /= 10 + } + time.Sleep(2 * time.Second) + return sum +} + +func createWorkerPool(numOfWorkers int) { + var wg sync.WaitGroup + for i := 0; i < numOfWorkers; i++ { + wg.Add(1) + go worker(&wg) + } + wg.Wait() + // 这里是否需要关闭results通道,是由稍后的range迭代这个通道决定的,不关闭这个通道会一直阻塞range,最终导致死锁 + close(results) +} + +// +// 最后需要关闭tasks通道,因为所有任务都分配完之后,没有任务再需要分配。 +// 当然,这里之所以需要关闭tasks通道,是因为worker()中使用了range迭代tasks通道 +// 如果不关闭这个通道,worker将在取完所有任务后一直阻塞,最终导致死锁。 +func allocate(numOfTasks int) { + for i := 0; i < numOfTasks; i++ { + rand.Seed(time.Now().UnixNano()) // 初始化随机数的资源库, 如果不执行这行, 不管运行多少次都返回同样的值 + randnum := rand.Intn(999) + task := Task{i, randnum} + tasks <- task + } + close(tasks) +} + +// 创建工作进程 +func worker(wg *sync.WaitGroup) { + defer wg.Done() + for task := range tasks { + result := Result{task, process(task.randnum)} + results <- result + } +} + +func getResult(done chan bool) { + for result := range results { + fmt.Printf("Task id %d, randnum %d , sum %d\n", result.task.ID, result.task.randnum, result.result) + } + done <- true +} + +func main() { + // 记录起始终止时间,用来测试完成所有任务耗费时长 + startTime := time.Now() + + numOfWorkers := 20 + numOfTasks := 100 + // 创建任务到任务队列中 + go allocate(numOfTasks) + // 创建工作池 + go createWorkerPool(numOfWorkers) + // 取得结果 + var done = make(chan bool) + go getResult(done) + <- done + endTime := time.Now() + diff := endTime.Sub(startTime) + fmt.Println("total time taken ", diff.Seconds(), "seconds") +} From 887559a923dd8741d517acf0e94c6b0092127e9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=B9=E5=B0=86=E5=B0=86?= Date: Mon, 9 Dec 2019 20:33:21 +0800 Subject: [PATCH 2/2] =?UTF-8?q?add:3&&4=E5=91=A8=E4=BD=9C=E4=B8=9A?= =?UTF-8?q?=E5=AE=8C=E6=95=B4=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../caojiangjiang/week3/leetcode/leetCode.go | 27 ++ homework/caojiangjiang/week3/theory/theory.md | 36 +++ .../caojiangjiang/week4/letCode/leetcode.go | 50 +++- homework/caojiangjiang/week4/theory/throry.md | 258 ++++++++++++++++++ .../caojiangjiang/week4/workPool/workPool.go | 11 + 5 files changed, 381 insertions(+), 1 deletion(-) diff --git a/homework/caojiangjiang/week3/leetcode/leetCode.go b/homework/caojiangjiang/week3/leetcode/leetCode.go index fb70a9a..e76740e 100644 --- a/homework/caojiangjiang/week3/leetcode/leetCode.go +++ b/homework/caojiangjiang/week3/leetcode/leetCode.go @@ -1 +1,28 @@ package leetcode +// +// 给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。 +// 来源:力扣(LeetCode) +// 链接:https://leetcode-cn.com/problems/container-with-most-water +// 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 +// +func maxArea(height []int) int { + value := 0 + max := 0 + left := 0 + right := len(height) - 1 + for { + if (left >= right) { + return max; + } + if (height[left] > height[right]) { + value = (right - left) * height[right] + right = right - 1 + } else { + value = (right - left) * height[left] + left = left + 1 + } + if (value > max) { + max = value + } + } +} diff --git a/homework/caojiangjiang/week3/theory/theory.md b/homework/caojiangjiang/week3/theory/theory.md index e69de29..830e204 100644 --- a/homework/caojiangjiang/week3/theory/theory.md +++ b/homework/caojiangjiang/week3/theory/theory.md @@ -0,0 +1,36 @@ +go语言中init函数用于包(package)的初始化,该函数是go语言的一个重要特性。 + +有下面的特征: + +1 init函数是用于程序执行前做包的初始化的函数,比如初始化包里的变量等 + +2 每个包可以拥有多个init函数 + +3 包的每个源文件也可以拥有多个init函数 + +4 同一个包中多个init函数的执行顺序go语言没有明确的定义(说明) + +5 不同包的init函数按照包导入的依赖关系决定该初始化函数的执行顺序 + +6 init函数不能被其他函数调用,而是在main函数执行之前,自动被调用 +main函数 + +Go语言程序的默认入口函数(主函数):func main() +函数体用{}一对括号包裹。 + +func main(){ + //函数体 +} +init函数和main函数的异同 + +相同点: + 两个函数在定义时不能有任何的参数和返回值,且Go程序自动调用。 +不同点: + init可以应用于任意包中,且可以重复定义多个。 + main函数只能用于main包中,且只能定义一个。 +两个函数的执行顺序: +对同一个go文件的init()调用顺序是从上到下的。 +对同一个package中不同文件是按文件名字符串比较“从小到大”顺序调用各文件中的init()函数。 +对于不同的package,如果不相互依赖的话,按照main包中"先import的后调用"的顺序调用其包中的init(),如果package存在依赖,则先调用最早被依赖的package中的init(),最后调用main函数。 + +如果init函数中使用了println()或者print()你会发现在执行过程中这两个不会按照你想象中的顺序执行。这两个函数官方只推荐在测试环境中使用,对于正式环境不要使用。 \ No newline at end of file diff --git a/homework/caojiangjiang/week4/letCode/leetcode.go b/homework/caojiangjiang/week4/letCode/leetcode.go index eaa93c4..27a16cf 100644 --- a/homework/caojiangjiang/week4/letCode/leetcode.go +++ b/homework/caojiangjiang/week4/letCode/leetcode.go @@ -1 +1,49 @@ -package letCode +package main + +import "fmt" + +// +// 在二维空间中有许多球形的气球。对于每个气球,提供的输入是水平方向上,气球直径的开始和结束坐标。 +// 由于它是水平的,所以y坐标并不重要,因此只要知道开始和结束的x坐标就足够了。 +// 开始坐标总是小于结束坐标。平面内最多存在104个气球。 +// +// 一支弓箭可以沿着x轴从不同点完全垂直地射出。 +// 在坐标x处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足  xstart ≤ x ≤ xend,则该气球会被引爆。 +// 可以射出的弓箭的数量没有限制。 弓箭一旦被射出之后,可以无限地前进。我们想找到使得所有气球全部被引爆,所需的弓箭的最小数量。 +// +//来源:力扣(LeetCode) +//链接:https://leetcode-cn.com/problems/minimum-number-of-arrows-to-burst-balloons +//著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 +// +// 思路:根据最小的进行排序 +// +func main() { + points := [][]int{{3,9}, {7,12}, {3,8}, {6,8}, {9,10}, {2, 9}, {0, 9}, {3, 9}, {0, 6}, {2, 8}} + result := findMinArrowShots(points) + fmt.Println(result) +} + +func findMinArrowShots(points [][]int) int { + if (len(points) == 0) { + return 0 + } + for i := 0; i < len(points); i++ { + for j := 0; j < len(points) - 1 - i; j ++ { + if points[j][1] > points[j + 1][1] { + tmp := points[j] + points[j] = points[j + 1] + points[j + 1] = tmp + } + } + } + min := points[0][1] + result := 1 + for i := 1; i < len(points); i++ { + if points[i][0] <= min { + continue + } + result += 1 + min = points[i][1] + } + return result +} \ No newline at end of file diff --git a/homework/caojiangjiang/week4/theory/throry.md b/homework/caojiangjiang/week4/theory/throry.md index e69de29..4ab040f 100644 --- a/homework/caojiangjiang/week4/theory/throry.md +++ b/homework/caojiangjiang/week4/theory/throry.md @@ -0,0 +1,258 @@ + +曾经学习python的时候,记得书上说 dict 是 python的 horsepower(动力)。然后,Slice 和 Map 又何尝不是 golang 的 workhorse 呢? +Array 是值类型,Slice 和 Map 是引用类型。他们是有很大区别的,尤其是在参数传递的时候。 +另外,Slice 和 Map 的变量 仅仅声明是不行的,必须还要分配空间(也就是初始化,initialization) 才可以使用。 +第三,Slice 和 Map 这些引用变量 的 内存分配,不需要你操心,因为 golang 是存在 gc 机制的(垃圾回收机制) +Array 的用法 +数组的声明(这里就是定义,给数据存储分配了空间): +var arrayName [arraySize] dataType + +如果数组定义好之后, 没有给数组元素指定值,那么所有元素被自动初始化为零值。 + +数组的初始化 +var a = [10]int {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} //定义数组的时候,直接初始化 + +var b = [10]int {1, 2, 3, 4} //部分元素初始化, 其余元素零值 + +var c = [...]int {1, 2, 3, 4, 5} //由初始化列表决定数组长度,不可省去标识符 "...",否则将变成切片Slice + +var d = [10]{2:4, 5:7} //可以按照下标来进行初始化 +数组的访问,可以直接按照下标进行访问 +数组的遍历: +package main +import( + "fmt" +) +func main() { + var f = [20]int {1, 1} + for i := 2; i < 20; i++ { + f[i] = f[i-1] + f[i-2] + } + for i := 0; i < 20; i++ { //采用下标进行遍历 + if i % 5 == 0 { + fmt.Printf("\n") + } + fmt.Printf("f[%2d] = %4d",i , f[i]) + } +} +也可以采用 range 关键字进行遍历: + +func main() { + var f = [20]int {1, 1} + for i := 2; i < 20; i++ { + f[i] = f[i-1] + f[i -2] + } + for i , v := range f { //采用 range 关键字 进行遍历 + fmt.Printf("f[%2d] = %4d", i, v) + } +} +多维数组 +var a [3][4]int +初始化 +var a = [3][4]int {{1,2}, {1,2,3,4}, {2,3, 4}} +多维数组遍历 +/*找到二维数组中的最大元素*/ + package main + import "fmt" + func main() { + var i, j, row, col, max int + var a = [3][4]int {{1, 3, 7, 3}, {2, 3, 7 , 9}, {22, 3, 5, 10}} + max = a[0][0] + for i := 0; i < = 2; i ++ { + for j := 0; j <= 3; j++ { + if a[i][j] > max { + max = a[i][j] + row = i + col = j + } + } + } + fmt.Println("max = %d, row = %d, col = %d\n", max, row, col) + } +Slice 的用法 +Slice 的声明(没有分配内存) + `var s1 []int` +在创建切片的时候,不要指定切片的长度。(否则就成了数组) +切片的类型可以是Go 语言的任何基本数据类型(也包括 引用类型和 Struct 类型) +当一个切片被声明之后,没有初始化的时候,这个 s1 默认的值是 nil。切片的长度是0。可以使用内建函数 len() 获得切片的长度,使用内建函数 cap() 获得切片的容量。 +Slice 的创建 (分配了内存) +三种创建方式: 基于底层数组创建,直接创建,或者 make() 函数创建 + +基于底层数组创建 slice +var slice1 []int //声明但是不分配空间 +slice1 = array[start:end] //这里不包含 end +slice2 := array[:] // 引用全部的元素 +slice3 := array[0:len(array)] +var slice4 []int +sliec34 = array //引用全部的元素 +直接创建 slice +在声明的时候,直接初始化。 + +var slice1 = []int {1 ,2, 3, 4, 5} +make() 函数创建 slice +var slice1 = make([]int, 5) //长度和容量都是 5 +var slice2 = make([]int, 5, 10) //容量是5. +Slice 的 访问和遍历 +采用下标进行访问,采用 range 进行遍历。 + +packge main +import "fmt" +func main() { + var slice1 = []int {1, 2,3 , 4, 5} + //使用下标访问 slice + for i := 0; i <=4; i++ { + fmt.Println("slice[%d] = %d", i, slice[i]) + } + fmt.Println() + //使用range 进行遍历 + for i, v := range slice { + fmt.Println("slice[%d] = %d", i, v) + } + } +Slice 的操作 +Slice 中的切片的元素,可以动态的添加和删除,所以操作起来要比数组更加方便。 + +切片元素的增加 +采用内建函数 append() 向切片尾部,增加新的元素, 这些元素保存到底层的数组。 + +append() 并不会影响原来的切片的属性,(原来切片的长度和cap) +append() 将会返回更新后的切片的对象。 +append() 是个变参函数,可以一次性添加多个对象。 +append() 添加元素的个数超过 切片的 cap() 的时候,那么底层会 重新分配一个 “足够大” 的内存,一般来说是将原来的内存空间扩大二倍,然后将数据复制到新的内存中去, 原来的空间会保留 (供原先切片使用)(底层数组变化这个问题,应该关注一下) +举例如下: +package main + +import "fmt" + +func main() { + //使用make 创建 切片 + var slice1 = make([]int, 3, 6) + // 使用 append 添加元素,并且未超出 cap + slice2 := append(slice1, 1, 2, 3) + // 使用 append 添加元素,并且超出 cap. 这个时候底层数组会变化,新增加的元素只会添加到新的底层数组,不会覆盖旧的底层数组。 + slice3 := append(slice1, 4, 5, 6, 7) + slice1[0] = 10 + fmt.Printf("len = %d cap = %d %v\n", len(slice1), cap(slice1), slice1) + fmt.Printf("len = %d cap = %d %v\n", len(slice2), cap(slice2), slice2) + fmt.Printf("len = %d cap = %d %v\n", len(slice3), cap(slice3), slice3) + +} + +程序输出是: +len = 3 cap = 6 [10 0 0] +len = 6 cap = 6 [10 0 0 1 2 3] // 这里的[1, 2, 3] 没有被 [4, 5, 6]覆盖 +len = 7 cap = 12 [0 0 0 4 5 6 7] //这里第一个元素没有变成10,并且容量变成原来的2倍。 +切片元素的复制 +使用切片长时间引用超大的底层数组,会导致严重的内存浪费现象。 可以新建一个小的slice 对象,然后将所需要的数据复制过去,这样子就不会引用底层数组,直接拷贝了数据,这就是需求。函数 copy()可以 在切片之间复制元素。 + +copy() 可以复制的元素数量取决于 复制方 和 被复制方的最小长度。 +同一个底层数组之间的 元素复制,会导致元素重叠问题。 +package main + +import "fmt" + +func main() { + var slice1 = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + var slice2 = make([]int, 3, 5) + var n int + n = copy(slice2, slice1) // just copy three elements + fmt.Println(n, slice2, len(slice2), cap(slice2)) + + slice3 := slice1[3:6] //二者引用同一个底层数组 + n = copy(slice3, slice1[1:5]) //所以,copy的时候发生元素重叠 + fmt.Println(n, slice1, slice3) + +} +程序输出为: +3 [1 2 3] 3 5 +3 [1 2 3 2 3 4 7 8 9 10] [2 3 4] //可以看到元素重叠 +Map 的用法 +map 存储的是 键值对(key-value)。是一个无序的数据的集合,通过键来进行索引得到对应的值。 这种方式可以加快查找速度。Map 通常称为 字典(dictionary) 或者哈希表(Hash table)。Map 现在是很多语言的标配。 + +字典的声明 +字典名称,“键”类型, “值”类型 +var mapName map[keyType]valueType +注意: + +不需要给字典指定长度,字典的长度会在初始化或者创建的过程中动态增长 +Key 必须是能支持 比较运算符(==, !=)的数据类型,比如 整数,浮点数,指针,数组,结构体,接口等。 而不能是 函数,字典,切片这些类型。 +Value 类型 可以是Go语言的任何基本数据类型。 +var map1 map[string]int +字典的初始化 和 创建 +字典 声明好之后,必须经过初始化或者创建 才能使用。未初始化或者创建的字典为 nil +可以使用“{}”来在声明的时候进行初始化。 +可是使用 make()来创建字典。 +创建或者初始化之后,就可以使用 “=”操作符来动态的向字典中添加数据项了。 +下面使用方式错误,编译不通过: +var map1 map[string]int +map1["key1"] = 2 //编译不通过,字典没有初始化或者创建 +下面使用方式正确 + +var map1 map[string]int {} //字典的初始化 +map1["key1"] = 1 + +var map2 map[string]int +map2 = make(map[string]int) //字典的创建 +map2["key2"] = 2 //使用 等号 添加数据项 +字典元素的查找 +v, OK := mapName[Key] //注意这里是 := +如果Key存在,将Key对应的Value赋值给v,OK== true. 否则 v 是0,OK==false. + +package main + +import "fmt" + +func main() { + var map1 = map[string]int{"key1": 100, "key2": 200} + // + v, OK := map1["key1"] + if OK { + fmt.Println(v, OK) + } else { + fmt.Println(v) + } + // 这里 不是 :=,是 = ,因为这些变量已经定义过了。 + v, OK = map1["key3"] + if OK { + fmt.Println(v, OK) + } else { + fmt.Println(v) + } + +} +输出为: +100 true +0 +字典项的删除 +go 提供了内置函数 delete() 来删除容器内的元素。 + +delete(map1, "key1") +如果key1值不存在,那么调用将什么也不发生,也不会产生副作用。 但是,如果传入的map 是一个 nil,那么将导致程序出现异常,这一点在写程序的时候特别注意。 + +package main + +import ( + "fmt" +) + +func main() { + var map1 = map[string]int{"key1": 100, "key2": 200, "key3": 300} + for k, v := range map1 { + fmt.Println(k, v) + if k == "key2" { + delete(map1, k) + } + if k == "key3" { + map1["key4"] = 400 + } + } + + fmt.Println(map1) +} +程序输出: +key2 200 +key3 300 +key1 100 +map[key1:100 key4:400 key3:300] //可以看到 map 是无序的。 + +https://studygolang.com/articles/2685 \ No newline at end of file diff --git a/homework/caojiangjiang/week4/workPool/workPool.go b/homework/caojiangjiang/week4/workPool/workPool.go index 2d630ae..dc2dadb 100644 --- a/homework/caojiangjiang/week4/workPool/workPool.go +++ b/homework/caojiangjiang/week4/workPool/workPool.go @@ -2,7 +2,10 @@ package main import ( "fmt" + "log" "math/rand" + "strconv" + "strings" "sync" "time" ) @@ -20,6 +23,7 @@ type Result struct { var tasks = make(chan Task, 10) var results = make(chan Result, 10) +var mutex sync.Mutex func process(num int) int { sum := 0 @@ -29,6 +33,13 @@ func process(num int) int { num /= 10 } time.Sleep(2 * time.Second) + // 加锁 + mutex.Lock() + fd_time := time.Now().Format("2006-01-02 15:04:05"); + sumStr := strconv.Itoa(sum) + fd_content:=strings.Join([]string{"======",fd_time,"=====",sumStr,"\n"},"") + log.Println("test-caojiangjiang-" + fd_content) + mutex.Unlock() return sum }