go并发编程基础

go被誉为互联网的C语言,它的同步其实还是基于传统语言,但是它的优势在于语言层面实现了协程支持,goroutine和channel组成的CSP模型,大大减少了并发编程开发难度,简单记下心得供有需要朋友参考。

go中开辟新线程使用go 关键字即可,同步方法分为传统方法和基于通道(channel)的方法,通道包括无缓冲和有缓冲通道,这些同步方法结合保证了go并发编程的灵活性。

 

1.传统方法

传统方法包括有锁编程和无锁编程,有锁指使用锁来保证代码同步,无锁编程使用原子编程来保证代码同步。

如下,开辟两个go线程,同时做加操作,因为涉及到竞态条件,对于全局变量count=0结果为1.

func test_count1(name string){
	defer g.Done()

	fmt.Println(name, " run...")

	v := count
	runtime.Gosched()

	v++
	count=v
	fmt.Println(name, "done... count=", v)
}

为了解决这个问题,就必须保证count增加过程中的不可被分割,使用加锁编程如下:

//锁同步
func test_count2(name string){
	defer g.Done()

	fmt.Println(name, " run...")

	m.Lock()
	v := count
	runtime.Gosched()

	v++
	count=v
	m.Unlock()

	fmt.Println(name, "done... count=", v)
}

也可以使用原子编程如下:

//原子同步
func test_count3(name string){
	defer g.Done()

	fmt.Println(name, " run...")

	atomic.AddInt32(&count, 1)
	runtime.Gosched()
}

2.无缓冲通道

无缓冲通道保证数据发送和接收是同时的,适合用于多个线程并发执行且相互之间有一定互斥关系的,每个时刻只能有一个线程持有通道数据。

比如如下,两个选手相互击球,通道是球,每个时刻一定只有一个选手能持有球,通过将数据传给通道来模拟传球过程,这也是channel的特质——将数据同步转成goroutine间的数据传递

//无缓冲通道1-球赛对打
func Play(name string, ball chan int){
	defer w.Done()

	for {
		v, ok := <-ball
		if !ok {
			fmt.Println(name, " win the game.")
			break
		}

		n := rand.Intn(100)
		if n%13 == 0 {
			fmt.Println(name, " lose the game.")
			close(ball)
			break
		}

		v++;
		fmt.Println(name, " kick the ball.")
		ball <- v
	}
}

func testPlay(){
	w.Add(2)

	ball := make(chan int)
	go Play("wenzhou", ball)
	go Play("wenwei", ball)
	ball <- 1

	w.Wait()
}

还可以比喻成接力赛,每次只有一个选手能够获取接力球,如下,通过传递数据给通道来模拟选手接力的过程

//无缓冲通道2-接力赛
func Run(ball chan int){
	v, ok := <-ball
	if !ok{
		return
	}

	fmt.Printf("Runner %d start to run\n", v)

	if v!=4 {
		v++
		fmt.Printf("Runner %d ready to run\n", v)
		go Run(ball)
	}

	//Running
	fmt.Println("...")
	time.Sleep(time.Duration(1)*time.Second)

	if v==4 {
		fmt.Printf("Runner %d Finished, race over\n", v)
		w.Done()
		return
	}

	fmt.Printf("Runner %d give ball to Runner %d\n", v-1, v)
	ball <- v
}

func testRun(){
	w.Add(1)

	ball := make(chan int)
	go Run(ball)
	ball <- 1

	w.Wait()
}

3.有缓冲通道

有缓冲通道的可以起到缓存和队列的作用,如下采用通道来完成任务排队的功能,很轻易的实现多路处理器的功能。

//带缓冲通道
func Worker(task chan string, worker int){
	defer w.Done()

	for  {
		t, ok := <-task
		if !ok {
			fmt.Printf("Worker %d Shutdwon\n", worker)
			break
		}

		fmt.Printf("Worker %d - Started:%s...\n", worker, t)
		time.Sleep(time.Duration(rand.Intn(100))*time.Microsecond)

		fmt.Printf("Worker %d - Completed:%s...\n", worker, t)
	}
}

func testWorker(){
	//缓冲通道
	tasks := make(chan string, NUM_TASK)

	//创建Worker,等待处理任务
	w.Add(NUM_WORKER)
	for i:=1; i<=NUM_WORKER; i++  {
		go Worker(tasks, i)
	}

	//抛任务处理
	for t:=1; t<=NUM_TASK; t++  {
		tasks <- fmt.Sprintf("Task-%d", t)
	}

	//关闭通道,等待处理完毕,注意go机制保证关闭通道后剩余任务可以被处理完毕
	close(tasks)
	w.Wait()
}

演示代码下载链接

原创,转载请注明来自http://blog.csdn.net/wenzhou1219

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值