1、创建时默认的stack的大小
JDK5以后JAVA Thread stack默认为1M
Groutine的Stack初始化为2K
2、和KSE(Kernel Space Entity)的对应关系
Java Thread是1:1
Groutine是M:N
JAVA的线程对应一个内核对象,一个线程的时候CPU调度的效率非常高,但是多个线程切换的时候需要切换内核对象,对比起来,go的多个线程对应一个内核对象,因此go的线程切换效率特别高
3、协程并发处理的机制
上图中,M代表的是系统线程,P是GO语言自己实现的协程处理器,G是一个协程任务,组成协程队列。
如果一个协程花费的时间特别长
机制1:协程的守护进程会记录协程处理器完成的协程数量,如果一段时间内,处理器完成的数量没有变化,就会往任务栈中插入一个标记,当前协程遇到非内联函数时,在任务栈中读到该标记,就会把该协程任务移到队列的末尾;
机制2:如果执行协程过程中遇到一个非CPU密集型任务,例如IO,需要中断等待时,协程处理器P会把自己移到另外一个可使用的系统线程中,继续执行队列中其他的协程任务。当中断的协程被重新唤醒后,会把自己重新加入到协程队列里或者全局等待队列中。其中,中断的协程会把context保存在协程对象里,当协程被唤醒后会将其重新读取到寄存器中,继续运行。
4、GO实现的协程的例子
func TestGroutine(t *testing.T) {
for i := 0; i < 10; i++ {
go func(i int) {
fmt.Println(i)
}(i)
}
time.Sleep(time.Millisecond * 50)
}
一个错误的协程示例:
func TestGroutine(t *testing.T) {
for i := 0; i < 10; i++ {
go func() {
fmt.Println(i)
}()
}
time.Sleep(time.Millisecond * 50)
}
错误的原因:i作为一个共享变量,如果不加锁机制,打印出来一定与预期不符,而正确的写法里面i是一个整形,会以值传递给协程后,被深度拷贝了一份,不会被其他的协程修改,所以可以被正确执行
如果对上面的错误进行加锁:
func TestGroutine(t *testing.T) {
var mutex sync.Mutex
for i := 0; i < 10; i++ {
go func() {
defer func(){
mutex.Unlock()
}
mutex.Lock()
fmt.Println(i)
}()
}
time.Sleep(time.Millisecond * 50)
}
也与预期结果一致
换成更高效的写法:
func TestGroutine(t *testing.T) {
var mutex sync.Mutex
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
go func() {
defer func(){
mutex.Unlock()
}
mutex.Lock()
fmt.Println(i)
wg.Done()
}()
}
wg.Wait()
}
准确的等待所有的协程执行完就退出,比傻傻的等好的多
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。