并发导致的问题


var money int = 0

func add(pint *int) {
	for i := 0; i < 100000; i++ {
		*pint++
	}
}

func AddMoney() {
	for i := 0; i < 1000; i++ {
		// 这里使用了协程调用,导致多个协程调用同一个资源,产生资源竞争,结果不正确
		go add(&money)
	}
	time.Sleep(time.Second*5)
	fmt.Println(money)
}

预期结果是100000000,但是由于add方法是协程方式调用的,存在并发的情况,导致结果不符合预期。



读写锁小例子


// 全局变量
var money int = 0

// 初始化锁
var lock *sync.RWMutex = new(sync.RWMutex)

func add2(pint *int) {
	// 为了避免资源竞争,操作时需要上锁
	// 加锁
	lock.Lock()
	for i := 0; i < 10000; i++ {
		*pint++
	}
	// 解锁
	lock.Unlock()
}

func main() {
	for i := 0; i < 1000; i++ {
		// 这里使用了协程调用,导致多个协程调用同一个资源,产生资源竞争,结果不正确
		go add2(&money)
	}
	time.Sleep(time.Second*5)
	fmt.Println(money)
}

这个例子里,写操作时加了写锁,也就是说整个写得过程是串行的,也就解决了上面的问题。



线程安全的队列


// 线程安全的队列
type Queue struct {

	// 数据
	data []interface{}

	// 长度
	len int

	// 锁
	lock *sync.Mutex
}

// 初始化线程安全队列
func NewQueue() *Queue {

	// 初始化结构体
	myQueue := new(Queue)

	// 开辟内存空间
	myQueue.data = make([]interface{}, 0, 10)

	// 初始化当前长度
	myQueue.len = 0

	myQueue.lock = new(sync.Mutex)

	return myQueue
}

// 获取队列长度,解决了线程安全
func (queue *Queue) Len() int {
	queue.lock.Lock()
	defer queue.lock.Unlock()
	return queue.len
}

// 判断是否空,解决了线程安全
func (queue *Queue) IsEmpty() bool {
	queue.lock.Lock()
	defer queue.lock.Unlock()
	return queue.len == 0
}

// 弹出第一个元素,解决了线程安全
func (queue *Queue) Shift() (el interface{}) {

	queue.lock.Lock()
	defer queue.lock.Unlock()

	// 为空
	if queue.Len() == 0 {
		return nil
	}

	// 第一个元素、数据
	el, queue.data = queue.data[0], queue.data[1:]

	return
}

// 压入元素,解决了线程安全
func (queue *Queue) Push(el interface{}) {
	queue.lock.Lock()
	defer queue.lock.Unlock()

	queue.data = append(queue.data, el)
	queue.len++

	return
}

// 获取第一个元素,解决了线程安全
func (queue *Queue) Front() interface{} {
	queue.lock.Lock()
	defer queue.lock.Unlock()

	// 为空
	if queue.Len() == 0 {
		return nil
	}

	return queue.data[0]
}

// 获取最后一个元素,解决了线程安全
func (queue *Queue) End() interface{} {
	queue.lock.Lock()
	defer queue.lock.Unlock()

	// 为空
	if queue.Len() == 0 {
		return nil
	}

	return queue.data[queue.Len()-1]
}

仿照了上面的思想,在每个读写操作的时都加上读写锁,这样子就可以实现队列的线程安全。