goroutine相关 | 青训营

58 阅读1分钟

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比使用线程要快一些,说明在高并发方面确实有所优势。


-这里我本来想输出机器所能支持的goroutinethread数量用于对比,通过下面代码:

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 

至此,这是我第一次青训文章,包含着比较多的个人理解,欢迎交流和批评指正。