十、同步
1. 计数器案例,同一个程序开俩个 进程A和进程B。并发执行 把数据库中符合条件的记录迁移到磁盘。用计数器记录 当前被处理的最大行号。
1. 第一步,读取计数器的值C
2. 第二步,从数据库中读取 C+10000 行数据。
3. 第三步, 遍历符合条件的数据,并形成新的数据集合
4. 第四步,将新的数据集合 存储到哦指定目录文件中。
5. 第五步,把计数器值+10000
6. 检查数据是否全部处理完成。是则退出,否则从第一步开始。
2. 原子操作,把执行过程中不能被中断的操作 为原子操作。
3. 临界区,把只能串行化访问或执行的资源或代码为 临界区。所有的系统调用都属于原子操作。
4. 原子操作和 临界区 是不同的概念,临界区 要求 一个访问者在临界区时,其他访问者不饿能被放进来。原子操作是不能被中断。原子操作需要芯片级别的支持,当今的cpu都提供了对原子的操作。
十一、管道
-
管道是一种半双工的通讯方式,它只能被用于父进程与子进程,以及同祖先的子进程之间的通讯。
-
shell命令 用到管道,ps aux | grep go
-
shell 为每个命令创建一个进程,然后把左边的命令 标准输出 用管道 与右边的命令的 标准输入 连接起来。
-
golang通过exec.command 执行命令+exec.command().StdoutPipe()以管道形式输出 到缓冲区bytes.Buffer
//1.输入定义系统命令 cmd0 := exec.Command("echo", "-n", "my first command from golang.") //2.定义命令执行标准输出管道 stdout0, _ := cmd0.StdoutPipe() //3.启动执行系统命令 if err := cmd0.Start(); err != nil { log.Fatalf("command err:%s", err.Error()) } var outputBuf bytes.Buffer for { output0 := make([]byte, 5) //4.通过标准输出管道 读出数据 打印 n, err := stdout0.Read(output0) if err != nil { if err == io.EOF { break } else { log.Fatalf("can not read data from pipe:%s", err.Error()) } } //5.当n< 5时表示数据读取完毕,当n>=5表示数据没有完全读完。需要for循环读取,直到err == io.EOF if n > 0 { outputBuf.Write(output0[:n]) } } log.Printf("data from pipe:%s\n", output0[:n])
-
golang通过exec.command 执行命令+exec.command().StdoutPipe()以管道形式输出 +通过带缓冲区的读取器输出
//1.输入定义系统命令 cmd0 := exec.Command("echo", "-n", "my first command from golang.") //2.定义命令执行标准输出管道 stdout0, _ := cmd0.StdoutPipe() //3.启动执行系统命令 if err := cmd0.Start(); err != nil { log.Fatalf("command err:%s", err.Error()) } //4.定义一个缓冲读取器, outputBuf := bufio.NewReader(stdout0) //5.第二个参数表示该行 是否被读完 output0, _, err := outputBuf.ReadLine() if err != nil { log.Fatalf("can not read data from pipe:%s", err.Error()) } log.Printf("data from pipe:%s\n", string(output0))
-
golang通过stdout1 := exec.Command().StdoutPipe()以管道形式输出 + 通过 bufio.NewReader(stdout1).WriteTo(stdout2) 读取,并写入第二个管道stdout2:= exec.Command().StdoutPipe()
//1.执行俩个命令 cmd1 := exec.Command("ps", "aux") cmd2 := exec.Command("grep", "nginx") //2.第一个命令以输出管道 stdout1, err := cmd1.StdoutPipe() if err != nil { log.Fatalf("stdoutpipe1 err:%v", err) } //3.启动第一个命令的执行 if err := cmd1.Start(); err != nil { log.Fatalf("cmd1 start err:%v", err) } //3.第一个管道内容传递给缓冲区1 outputBuf1 := bufio.NewReader(stdout1) //4.定义第二个命令的输入管道 stdin2, err := cmd2.StdinPipe() if err != nil { log.Fatalf("stdin2 err:%v", err) } //5.把第一个缓冲区1 写入第二个输入管道2 _, err = outputBuf1.WriteTo(stdin2) if err != nil { log.Fatalf("outputBuf1 write to stdin2 err:%v", err) } //6.定义第二个命令标准输出缓冲区2 var outputBuf2 bytes.Buffer cmd2.Stdout = &outputBuf2 if err := cmd2.Start(); err != nil { log.Fatalf("cmd2 start err:%v", err) } //7.关闭第二个输入管道 if err := stdin2.Close(); err != nil { log.Fatalf("cmd2 close err:%v", err) } //8.等待所有运行结束 if err := cmd2.Wait(); err != nil { log.Fatalf("cmd2 wait err:%v", err) } log.Printf("outputBuf2 :%s", outputBuf2.String()) /** 结果: 2022/09/17 20:27:11 outputBuf2 : root 1084 0.0 0.0 46504 1208 ? Ss 19:50 0:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf nginx 1086 0.0 0.0 49044 2052 ? S 19:50 0:00 nginx: worker process nginx 1091 0.0 0.0 49044 2052 ? S 19:50 0:00 nginx: worker process nginx 1094 0.0 0.0 49044 2052 ? S 19:50 0:00 nginx: worker process nginx 1096 0.0 0.0 49044 2052 ? S 19:50 0:00 nginx: worker process */
-
以上是匿名管道,相对应的是命名管道,任何程序都可以通过命名管道交换数据。命名管道以文件形式存在于文件系统中
-
//命名管道默认是阻塞的,单向的。 //1.创建命名管道myfifo1 mkfifo -m 644 myfifo1 //2.阻塞等待 myfifo1 命名管道数据到来。tee从标准输入设备读取数据,将内容输出到标准输出设备,并写入文件。 tee dst.log < myfifo1 & //3. 把dst.log内容写入管道myfifo1 cat dst.log > myfifo1
- 注意,命名管道 默认会 将其中一端 还未就绪的时候阻塞 另一端的进程
//注意,命名管道 默认会 将其中一端 还未就绪的时候阻塞 另一端的进程。 //1. os.pipe() 输出俩个结构,输入端;输出端 *os.File reader, writer, _ := os.Pipe() var wg sync.WaitGroup wg.Add(1) go func(file *os.File) { defer file.Close() defer wg.Done() if _, err := file.Write([]byte("i love you!")); err != nil { log.Fatalf("write pipe err:%v", err) } log.Printf("writer pipe succ.\n") }(writer) wg.Add(1) go func(file *os.File) { defer file.Close() defer wg.Done() output := make([]byte, 512) n, err := file.Read(output) if err != nil { log.Fatalf("reader pipe err:%v", err) } log.Printf("reader pipe:%s", string(output[:n])) }(reader) wg.Wait() /** 结果: 2022/09/17 20:54:17 writer pipe succ. 2022/09/17 20:54:17 reader pipe:i love you! */
-
匿名管道会在管道缓冲区被写满后 使写数据的 进程被阻塞。命名管道会在一段就绪前 阻塞 另一端的进程。
-
当有多个输入端同时写入数据的时候得考虑原子性,操作系统提供的管道使不提供原子操作支持的。
-
内存管道 go 提供的io.Pipe()
-
go channel 和 pipeline区别
不同 channel pipe 结构 stream of a go type stream of bytes 平台 specific type in golang unix like 缓冲区 depend on circumstances always buffered 特点 connect between goroutine between two process(std.in/out)