goroutine 相关内容
goroutine与线程比较
go因为其goroutine更为轻量级,相较于线程而言能够提高并发数,那么具体有多少呢?这是我一直的疑惑。 所以分别在个人云服务器、个人PC进行测试,go/python代码块如下:
func main() {
time1 := time.Now()
numThreads := 1000
var wg sync.WaitGroup
wg.Add(numThreads)
for i := 0; i < numThreads; i++ {
go func() {
// 执行一些并发任务
fmt.Println("Running goroutine")
wg.Done()
}()
}
wg.Wait()
time2 := time.Since(time1).Seconds()
fmt.Printf("All goroutines completed,%f s Past \n", time2)
}
//2vCPU云服务器输出:0.0022s
//个人PC:0.0042s
def task():
# 执行一些并发任务
print("Running thread")
numThreads = 1000
threads = []
start_time = time.time()
for i in range(numThreads):
t = threading.Thread(target=task)
t.start()
threads.append(t)
for t in threads:
t.join()
end_time = time.time()
execution_time = end_time - start_time
print("All threads completed")
print("Execution time:", execution_time, "seconds")
#云服务器Python耗时约:0.0869s
#个人PC耗时:0.3170s
都是1000个任务,使用goroutine比使用线程要快一些,说明在高并发方面确实有所优势。
-这里我本来想输出机器所能支持的goroutine和thread数量用于对比,通过下面代码:
maxGoroutines := runtime.GOMAXPROCS(0)
fmt.Println("Max goroutines:", maxGoroutines)
maxThreads = threading.active_count()
print("Max threads:", maxThreads)
但结果并不理想,这只会输出支持的最大协程和活跃的线程数,这里不太懂,欢迎交流 ,所以换成了上面的方法-规定任务数,观察耗时。
goroutine与channel
ok,现在关注与goroutine与channel通道。
-
本质与线程区别:协程工作于用户态,协程栈处于KB级内存开销,而线程工作与内核态,线程内包括多个协程,MB级别内存开销。
-
CSP(Communication Sequential Processes)要求goroutine之间以通信来达成共享内存,主要通过channel通道来完成通信。 而借助通道达成的 异步调用 避免主线程和其他goroutine的阻塞。
-
为了达成 并发安全 需要借助锁(如,Mutex)来控制访问,从而避免多goroutine共同访问变量造成不确定结果。
在课程中典型通信共享内存方式,在下面的代码中体现:
func ComSquare() {
src := make(chan int, 10)
dst := make(chan int, 10)
time1 := time.Now()
go func() {
defer close(src)
for i := 0; i < 5001; i++ {
src <- i
}
}()
go func() {
defer close(dst)
for i := range src {
dst <- i * i
}
}()
time2 := time.Since(time1)
for i := range dst {
fmt.Println(i)
}
fmt.Println("time cost:", time2)
}
//time cost: 6.089µs
在这里我测试过,5000以下的增长几乎不会增加耗时,这里应该能说明goroutine并行计算,不会增加耗时。
而在并发中,需要借助mutex对数据“上锁”从而保证并发安全,代码如下:
package main
import (
"fmt"
"sync"
"time"
)
var (
x int
mu sync.Mutex
)
func OperWithLock() {
for i := 0; i < 2000; i++ {
mu.Lock()
x += 1
mu.Unlock()
}
}
func OperWithoutLock() {
for i := 0; i < 2000; i++ {
x += 1
}
}
func main() {
x = 0
for i := 0; i < 5; i++ {
go OperWithLock()
}
time.Sleep(time.Second)
fmt.Printf("result:%d \n", x)
x = 0
for i := 0; i < 5; i++ {
go OperWithoutLock()
}
time.Sleep(time.Second)
fmt.Printf("result withoutlock:%d \n", x)
}
//result=>10000
//result withoutlock=>8922
至此,这是我第一次青训文章,包含着比较多的个人理解,欢迎交流和批评指正。