Go Language Advanced Study Notes

Coroutine

  1. Create a default when a stack of zoomed ⼩
  • After JDK5 Java Thread stack defaults to 1M
  • Groutine initialization of Stack zoomed ⼩ to 2K
  1. And correspondence between the KSE (Kernel Space Entity system threads)
  • Java Thread 是 1:1
  • Groutine is M: N

Thread switching cost big

image

image

Let me talk about the nature of coroutines is the thread user mode, the user has control of its authority, small footprint, low switching costs.

MPG again explain what that means.

M stands for kernel threads, and all should be placed on M G to run.

P represents a controller to schedule G M, which maintains a queue, it is needed to store all the scheduled G.

G represents a unit go routine.

Add a few common scheduling policy:

1, if an M into blocking it?
When a M OS thread blocked due io into operation, assuming that run on the G0 n M, P M that is bound to the rest of all would take to find a new G M. When M is recovered, in general, will take over from the other a P M, and the previously running thereon G0 P placed on the queue, thereby running G0. If P is not available, then get, put into the global global runqueue G0 queue, waiting to be scheduled so G0, then M enters the thread cache. All P will periodically check the global runqueue goroutine and run them, otherwise goroutine on global runqueue never executed.

2, if any busier M, M and some relatively free of it?
The P and G assigned tasks performed quickly finished (uneven distribution), which led to the busy processor P, P but also other tasks. In this case, P is first to take the global runqueue G. However, if there is no global runqueue task G, then P had to pick up some from other G P in to perform. In general, if P P from there to bring other tasks, the general took half run queue, which ensures that each OS thread can fully use.

3, if a G run for too long, leading to the subsequent G queue can not run it?
Start time, it will create a special thread sysmon, used to monitor and manage, within a cycle. First, a recording task for all P G counts schedtick, schedtick incremented after each performing a task G. If the check has not been incremented to schedtick, indicating that P G has been implementing the same task, if more than a certain period of time (10ms), plus a mark on the stack information G this task inside. Then the G task in the course of implementation, if you encounter a non-inline function call inside, it will check for this flag, then interrupted himself to add to their end of the queue, perform the next G. If you do not encounter a non-inline function (sometimes normal function will be optimized to small inline function) call, then it was tragic, G will always perform this task until the end of its own; if it is a cycle of death and GOMAXPROCS = 1, then congratulations, rammed live! Pro-test, it is true.

4, a G because the schedule is interrupted, then how to restore?
When the interrupt stack register information, save it to their G objects inside. When their turn to perform again, copy your saved information to the register stack inside, so then after running the last. (≧ ▽ ≦) /

func TestGroutine(t *testing.T) {
	for i := 0; i < 10; i++ {
		go func(i int) {
			//time.Sleep(time.Second * 1)
			fmt.Println(i) //值传递,协程间没有竞争关系
		}(i)
	}
	time.Sleep(time.Millisecond * 50)
}

//每次输出顺序不一致

Shared memory concurrency

  • 包package sync
    Mutex
    RWLock

  • Locking Operation

func TestCounterThreadSafe(t *testing.T) {
	var mut sync.Mutex
	counter := 0
	for i := 0; i < 5000; i++ {
		go func() {
			defer func() {
				mut.Unlock()
			}()
			mut.Lock()
			counter++
		}()
	}
	time.Sleep(1 * time.Second) //等所有协程执行完
	t.Logf("counter = %d", counter)
}
  • WaitGroup only after completion of all the things I wait to perform down
func TestCounterWaitGroup(t *testing.T) {
	var mut sync.Mutex
	var wg sync.WaitGroup
	counter := 0
	for i := 0; i < 5000; i++ {
		wg.Add(1) //新增一个我们要等待的任务
		go func() {
			defer func() {
				mut.Unlock()
			}()
			mut.Lock()
			counter++
			wg.Done() //有一个等待的任务已经完成
		}()
	}
	wg.Wait() //所有要等待的任务都已经完成
	t.Logf("counter = %d", counter)

}

CSP concurrency model

  • CSP model is proposed seventies of the last century, and is used to describe two separate entities through concurrent shared communication channel (pipe) concurrent model of communication. CSP is a first-class objects in the channel, it is not concerned with the entity sending the message, while using the channel concerned when sending messages.
Golang CSP
  • CSP is to borrow some concepts Golang model whom support concurrency theory, in fact, starting from the fact, go language did not fully realize all the theoretical model for CSP, just borrowed the process and channel these two concepts. process entity is reflected in the language that go goroutine is actually concurrent execution, data sharing is achieved through the communication channel between each entity.
Channel
  • Golang use the CSP channel concept. channel is created separately and can be transferred between processes, its communication mode is similar to boss-worker mode, one entity by sending a message to the channel, then the channel listening process entity is between two entities anonymous, this intermediate entity to achieve decoupling, which channel is a synchronous message is sent to the channel, the final is sure to be consumed another entity, in fact, is the realization of the principle of a blocking message queue.
// 做一个任务service(50ms)
func service() string {
	time.Sleep(time.Millisecond * 50)
	return "Done"
}

// 不相关的任务(100ms)
func otherTask() {
	fmt.Println("working on something else")
	time.Sleep(time.Millisecond * 100)
	fmt.Println("Task is done.")
}

// 这样调用两个任务是串行的
func TestService(t *testing.T) {
	fmt.Println(service())
	otherTask()
	//串行输出任务1,任务2结果
}

/*************** CSP 改进版 ********************/

// 返回结果的时候把channel返回回去,当外面需要的时候可以去channel等待
func AsyncService() chan string {
	retCh := make(chan string) //阻塞性channel
	//retCh := make(chan string, 1) // 非阻塞性
	//启用一个协程去运行
	go func() {
		ret := service()
		fmt.Println("returned result.")
		retCh <- ret //运行完把结果返回到channel,如果其他地方没有取这个消息,整个协程被阻塞,如果用buffer channel则不会被阻塞
		fmt.Println("service exited.")
	}()
	return retCh
}

//
func TestAsynService(t *testing.T) {
	retCh := AsyncService() 
	otherTask() //(结果是这行先被执行好)
	fmt.Println(<-retCh) //需要结果的时候从channel取出数据
	time.Sleep(time.Second * 1)
}

Guess you like

Origin www.cnblogs.com/jaychan/p/12609100.html