通常可以使用锁、信号量(Semaphores)、屏障(Barrier)和条件变量(Condition Variable)机制来实现线程同步。根据不同的并发场景分为很多不同类型的锁,有互斥锁(Mutex)、读写锁(RwLock)和自旋锁(Spinlock)等。锁的作用是可以保护临界区,同时达到同步和互斥的效果。不同的锁表现不同,比如互斥锁,每次只允许单个线程访问临界资源;读写锁可以同时支持多个线程读或单个线程写;自旋锁和互斥锁类似,但当获取锁失败时,它不会让线程睡眠,而是不断地轮询直到获取锁成功。
信号量可以在线程间传递信号,也叫作信号灯,它可以为资源访问进行计数。信号量是一个非负整数,所有通过它的线程都会将该整数减 1,如果信号量为 0,那么其他线程只能等待。当线程执行完毕离开临界区时,信号量会再次加1。当信号量只允许设置0和1时,效果相当于互斥锁。
屏障可以让一系列线程在某个指定的点进行同步。通过让参与指定屏障区域的线程等待,直到所有参与线程都到达指定的点。而条件变量用来自动阻塞一个线程,直到出现指定的条件,通常和互斥锁配合使用。通过一些锁机制,比如互斥锁,也可以用来避免数据竞争。本质上,是通过锁来保护指定区域的原子性的。有些语言也提供了原子类型来保证原子性,比如Java、C++以及Rust。具有原子性的操作一定是不可分割的,要么全部完成,要么什么都不做。
[go] 解决:concurrent write to websocket connection
出现这个问题是因为并发的调用了网页链接库的WriteMessage方法
在websocket连接上有多个groutinue同时调用写方法
go官方的解释:
并发
连接支持一个并发读取器和一个并发写入器。
应用程序负责确保不超过一个 goroutine 同时调用写入方法(NextWriter、SetWriteDeadline、WriteMessage、WriteJSON、EnableWriteCompression、SetCompressionLevel),并且不超过一个 goroutine 调用读取方法(NextReader、SetReadDeadline、ReadMessage、ReadJSON、SetPongHandler) , SetPingHandler) 并发。
Close 和 WriteControl 方法可以与所有其他方法可以并发调用。
每一个请求都是一个groutine,如果有多个groutine同时请求并且要写回数据,就会出现这个错误
一定要加上锁,并且在业务上避免多个同时调用
为了解决这一问题,我定义了一个全局的读写互斥锁(sync.RWMutex) 。
var WSWriteMux sync.RWMutex
在所有开始调用WriteMessage方法之前,先上锁。
调用完WriteMessage方法之后,再解锁。
WSWriteMux.Lock()
singlemap.wsconn.WriteMessage(websocket.TextMessage, []byte(base64.StdEncoding.EncodeToString([]byte(nmsg))))
WSWriteMux.Unlock()