博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Go多线程与channel通信
阅读量:7224 次
发布时间:2019-06-29

本文共 4785 字,大约阅读时间需要 15 分钟。

 
//1. 低水平实现 生产者-消费者多线程
package mainimport (	"container/list"	"fmt"	"math/rand"	"runtime"	"strconv"	"sync")type (	Producer interface {		produceApple(s *superMakert) //生产苹果	}	Consumer interface {		buyApple(s *superMakert) //超市买苹果	}	superMakert struct {		//list包 实现了双向链表		apples *list.List	}	ProducerImpl struct {		Producer //实现生产苹果的接口	}	ConsumerImpl struct {		Consumer //实现消费苹果的接口	}	Apple struct {		weight float64 //重量		number int     //编号	})//实现接口里面的方法func (p *ProducerImpl) produceApple(s *superMakert) {	for {		for s.apples.Len() > 0 {			//Gosched使当前go程放弃处理器,以让其它go程运行。			//它不会挂起当前go程,因此当前go程未来会恢复执行			runtime.Gosched()		}		apple := &Apple{			number: rand.Intn(10000),			weight: rand.Float64(),		}		s.apples.PushBack(apple)		fmt.Printf("生产了编号为%d,重量为%0.2f的苹果\r\n", strconv.Itoa(apple.number), apple.weight)	}}//实现接口里面的方法func (c *ConsumerImpl) buyApple(s *superMakert) {	for {		if s.apples.Len() <= 0 {			runtime.Gosched()		}		element := s.apples.Back()		apple := element.Value.(*Apple)		fmt.Printf("买了编号为%d,重量为%0.2f的苹果\r\n", strconv.Itoa(apple.number), apple.weight)		s.apples.Remove(element)	}}func main() {	//WaitGroup用于等待一组线程的结束。父线程调用Add方法来设定应等待的线程的数量。	//每个被等待的线程在结束时应调用Done方法。同时,主线程里可以调用Wait方法阻塞至所有线程结束	wg := new(sync.WaitGroup)	p := new(ProducerImpl)	c := new(ConsumerImpl)	a := &superMakert{apples: list.New()}	wg.Add(2)	go func() { //goroutine		p.produceApple(a)		wg.Done()	}()	go func() {		c.buyApple(a)		wg.Done()	}()	wg.Wait()}

 

运行程序报错:  invalid  memory address  or nil pointer dereference 

错误定位:

if s.apples.Len() <= 0 {	runtime.Gosched()}

这里涉及到自旋锁的概念 这里参考 https://www.cnblogs.com/cyyljw/p/8006838.html

 

自旋锁可以使线程在没有取得锁的时候,不被挂起,而转去执行一个空循环,(即所谓的自旋,就是自己执行空循环),若在若干个空循环后,线程如果可以获得锁,则继续执行。若线程依然不能获得锁,才会被挂起。

使用自旋锁后,线程被挂起的几率相对减少,线程执行的连贯性相对加强。因此,对于那些锁竞争不是很激烈,锁占用时间很短的并发线程,具有一定的积极意义,但对于锁竞争激烈,单线程锁占用很长时间的并发程序,自旋锁在自旋等待后,往往毅然无法获得对应的锁,不仅仅白白浪费了CPU时间,最终还是免不了被挂起的操作 ,反而浪费了系统的资源。

 

错误的原因是 : 空指针 。上面代码多个线程同时去访问,没有准确的判断 s.apple.Len() 是否是完全小于0的状态,也就是说在s.apples.Len()为0 的时候程序还是往下执行了导致了空指针。

优化代码为: 用循环一直去判断长度是否为空 但是使用这种方式 在单线程的时候没有意义而且非常消耗CPU的资源

for s.apples.Len() <= 0 {	runtime.Gosched()}

  

  运行结果如下:  (两个线程同时运行)

 

总结: 这种方式实现太粗燥。

后续实现高级一点的go 多线程和channel通信

 

2. 使用channel 实现 多线程

package mainimport (	"fmt"	"math/rand"	"strconv"	"sync")//生产者-消费者 channel 实现 一边生产 一边消费type (	Producer interface {		produceApple(s *superMakert) //生产苹果	}	Consumer interface {		buyApple(s *superMakert) //超市买苹果	}	superMakert struct {		//list包 实现了双向链表		//apples *list.List		apples chan Apple	}	ProducerImpl struct {		Producer //实现生产苹果的接口	}	ConsumerImpl struct {		Consumer //实现消费苹果的接口	}	Apple struct {		weight float64 //重量		number int     //编号	})//实现接口里面的方法func (p *ProducerImpl) produceApple(s *superMakert) {	for {		apple := Apple{			number: rand.Intn(10000),			weight: rand.Float64(),		}		fmt.Printf("生产了编号为%s,重量为%0.2f的苹果\r\n", strconv.Itoa(apple.number), apple.weight)		s.apples <- apple	}}//实现接口里面的方法func (c *ConsumerImpl) buyApple(s *superMakert) {	for {		element, _ := <-s.apples		fmt.Printf("买了编号为%s,重量为%0.2f的苹果\r\n", strconv.Itoa(element.number), element.weight)	}}func main() {	//WaitGroup用于等待一组线程的结束。父线程调用Add方法来设定应等待的线程的数量。	//每个被等待的线程在结束时应调用Done方法。同时,主线程里可以调用Wait方法阻塞至所有线程结束	wg := new(sync.WaitGroup)	p := new(ProducerImpl)	c := new(ConsumerImpl)	a := &superMakert{apples: make(chan Apple)}	wg.Add(2)	go func() { //goroutine		p.produceApple(a)		wg.Done()	}()	go func() {		c.buyApple(a)		wg.Done()	}()	wg.Wait()}

  

3. 多生产多消费

package mainimport (	"fmt"	"math/rand"	"strconv"	"sync")//生产者-消费者 channel 实现 一边生产 一边消费type (	Producer interface {		produceApple(s *superMakert) //生产苹果	}	Consumer interface {		buyApple(s *superMakert) //超市买苹果	}	superMakert struct {		//list包 实现了双向链表		//apples *list.List		apples chan Apple	}	ProducerImpl struct {		Producer //实现生产苹果的接口	}	ConsumerImpl struct {		Consumer //实现消费苹果的接口	}	Apple struct {		weight float64 //重量		number int     //编号	})//实现接口里面的方法func (p *ProducerImpl) produceApple(s *superMakert) {	for {		apple := Apple{			number: rand.Intn(10000),			weight: rand.Float64(),		}		fmt.Printf("生产了编号为%s,重量为%0.2f的苹果\r\n", strconv.Itoa(apple.number), apple.weight)		s.apples <- apple	}}//实现接口里面的方法func (c *ConsumerImpl) buyApple(s *superMakert) {	for {		element, ok := <-s.apples		if ok {			fmt.Printf("买了编号为%s,重量为%0.2f的苹果\r\n", strconv.Itoa(element.number), element.weight)		} else {			fmt.Printf("没有买到苹果")		}	}}func main() {	//WaitGroup用于等待一组线程的结束。父线程调用Add方法来设定应等待的线程的数量。	//每个被等待的线程在结束时应调用Done方法。同时,主线程里可以调用Wait方法阻塞至所有线程结束	wg := new(sync.WaitGroup)	p := new(ProducerImpl)	c := new(ConsumerImpl)	a := &superMakert{apples: make(chan Apple, 20)}	for i := 0; i < 20; i++ {		wg.Add(1)		go func() { //goroutine			p.produceApple(a)			wg.Done()		}()	}	for j := 0; j < 20; j++ {		wg.Add(1)		go func() {			c.buyApple(a)			wg.Done()		}()	}	wg.Wait()}

  

  

 

转载于:https://www.cnblogs.com/wpgraceii/p/10441036.html

你可能感兴趣的文章
windows server 2008 域控安装
查看>>
编写高质量代码:改善Java程序的151个建议(第1章:JAVA开发中通用的方法和准则___建议6~10)...
查看>>
Oracle查看和修改连接数(进程/会话/并发等等)
查看>>
【SpringMVC学习06】SpringMVC中的数据校验
查看>>
Laravel错误与日志处理
查看>>
微信小程序开发教程第七章:微信小程序编辑名片页面开发
查看>>
Java并发编程:Java ConcurrentModificationException异常原因和解决方法
查看>>
浅谈iOS中MVVM的架构设计
查看>>
node.js 中模块的循环调用问题详解
查看>>
ActiveReports 报表应用教程 (6)---分组报表
查看>>
OLEDB操作Excel
查看>>
struts2的json-default和struts-default的区别
查看>>
java中<> 的用法
查看>>
IIS 下配置无后缀的URL ReWrite
查看>>
对Asp.net Mvc 和 jQuery UI使用者的一些忠告
查看>>
Silverlight开发历程—动画(实现跑马灯效果)
查看>>
怎么说???
查看>>
[原]Windows批处理命令学习一
查看>>
AaronYang风格 C语言挑讲[一][基本入门]
查看>>
【oneday_onepage】——The Secret Of Steve<1>
查看>>