1,进程和线程
操作系统会为该应用程序创建一个进程。作为一个应用程序,它像一个为所有资源而运 行的容器。这些资源包括内存地址空间、文件句柄、设备和线程。
线程是操作系统调度的一种执行路径,用于在处理器执行我们在函数中编写的代码。一 个进程从一个线程开始,即主线程,当该线程终止时,进程终止。这是因为主线程是应 用程序的原点。然后,主线程可以依次启动更多的线程,而这些线程可以启动更多的线 程。
无论线程属于哪个进程,操作系统都会安排线程在可用处理器上运行。每个操作系统都 有自己的算法来做出这些决定。
2, goroutine和并行性
Go语言层面支持的go关键字,可以快速的让一个函数创建为goroutine,我们可以认 为main函数就是作为goroutine执行的。操作系统调度线程在可用处理器上运行,Go 运行时调度goroutine在绑定到单个操作系统线程的逻辑处理器中运行(P)。即使使用这 个单一的逻辑处理器和操作系统线程,也可以调度数十万goroutine以惊人的效率和性 能并发运行。
Concurrency is not Parallelism.
并发不是并行。并行是指两个或多个线程同时在不同的处理器执行代码。如果将运行时 配置为使用多个逻辑处理器,则调度程序将在这些逻辑处理器之间分配goroutine,这 将导致goroutine在不同的操作系统线程上运行。但是,要获得真正的并行性,您需要 在具有多个物理处理器的计算机上运行程序。否则,goroutine将针对单个物理处理器 并发运行,即使G。运行时使用多个逻辑处理器。
3,将并发性留给调用者
这两个api有啥区别?
- 将目录读取到一个slice中,然后返回整个切片,或者如果出现错误,则返回错误,这是同步调用的,ListDireclory的调用方法阻塞,直到读取所有目录条目,根据目录的大小这可能需要很长时间,并且会分配大量内存来构建目录条目名称的slice
- ListDirectory返回一个chan string ,将通过该chan传递目录。当通道关闭时,这表示不再目录,由于在ListDirectory返回后发生通道的填充,ListDirectory可能内部启动gorouting填充通道
ListDirectory chan版本还有两个问题:
- •通过使用一个关闭的通道作为不再需要处理的项目的信号,ListDirectory无法告诉调用者通过 通道返回的项目集不完整,因为中途遇到了错误。调用方无法区分空目录与完全从目录读取的 错误之间的区别。这两种方法都会导致从ListDirectory返回的通道会立即关闭。
- •调用者必须持续从通道读取,直到它关闭,因为这是调用者知道填充chan的goroutine已经 停止的唯一方法。这对ListDirectory的使用是一个严重的限制,调用者必须花时间从通道读取 数据,即使它可能已经收到了它想要的答案。对于大中型目录,它可能在内存使用方面更为高 校,但这种方法并不比原始的基于slice的方法快。
filepath.WalkDir也是类似的模型,如果函数启动goroutine,则必须向调用方提供显式停止该 goroutine的方法。通常,将异步执行函数的决定权交给该函数的调用方通常更容易。
在这个例子中,goroutine泄以在code review快速识别出来。不幸的是,生产代码中 的goroutine泄漏通常更难找到。我无法说明goroutine泄漏可能发生的所有可能方式, 您可能会遇到:
泄露2=》
下面代码是一个同步调用阻塞执行过程
经过某同学改造后并发代码,实际上也存在内存泄露问题,假如ctx.Done()先执行,那么上面的协程就会内存泄露。当然改成长度为1的chan可以解决。
通过将serveApp和serveDebug处理程序分解为各自的函数,我们将它们与 main.main解脑,我们还遵循了上面的建议,并确保serveApp和serveDebug将它们 的并发性留给调用者。如果serveApp返回,则main.main将返回导致程序关闭,只能靠类似 suoervisor进程管理来重新启动.
优化后:
ListenAndServer 返回 nil error,最终 main.main 无法退出。log.Fatal 调用了 os.Exit, 会无条件终止程序;defers不会被调用到。
更高级的优化代码:
使用sync.WaitGroup来追踪每一个创建的goroutine
将wg.Wait()操作托管到其它goroutine,owener goroutine使用context处理超时.
本文来自博客园,作者:孙龙-程序员,转载请注明原文链接:https://www.cnblogs.com/sunlong88/p/16062909.html