内容简介:下面是使用的结构体接口抽象定义,其实就是将结构体存进一个上面的那么问题来了。当我调用
下面是使用的结构体接口抽象定义,其实就是将结构体存进一个 map
里。由于是读写都比较频繁,我加了读写锁。
// add progress listener. func (upload *UploaderGateway) AddProgress(key string, v ProgressListener) { upload.mutex.Lock() defer upload.mutex.Unlock() upload.ProgressMap[key] = v } //get progress listener. func (upload *UploaderGateway) GetProgress(key string) (v ProgressListener, err error) { upload.mutex.RLock() defer upload.mutex.RUnlock() progressListener, ok := upload.ProgressMap[key] if !ok { return nil, errors.New("Get ProgressListener Not Found") } listener := progressListener.GetFormat() return &listener, nil } //delete progress listener. func (upload *UploaderGateway) DeleteProgress(key string) { upload.mutex.Lock() defer upload.mutex.Unlock() delete(upload.ProgressMap, key) }
结构体定义
// 定义进度条监听器。 type OssProgressListener struct { FileSha1 string `json:"file_sha1"` //file sha1 FileSize int64 `json:"file_size"` //file size ConsumedBytes int64 `json:"consumed_bytes"` //consumed bytes mutex *sync.RWMutex } // set consumed bytes. func (listener *OssProgressListener) SetConsumedBytes(value int64) ProgressListener { listener.mutex.Lock() defer listener.mutex.Unlock() listener.ConsumedBytes = value return listener } // 定义进度变更事件处理函数。 func (listener *OssProgressListener) ProgressChanged(event *oss.ProgressEvent) { listener.SetConsumedBytes(event.ConsumedBytes) //pretty.Printf("event: %# v\n", event) //pretty.Printf("listener: %# v\n", listener) switch event.EventType { case oss.TransferStartedEvent: fmt.Printf("传输已启动,已用字节数: %d, 总计字节: %d.\n", event.ConsumedBytes, listener.FileSize) case oss.TransferDataEvent: //if event.ConsumedBytes == 0 || listener.FileSize == 0 { // fmt.Printf("传输数据,消耗字节: %d, 总计字节: %d, %d%%.\n", // event.ConsumedBytes, listener.FileSize, event.ConsumedBytes) //} else { // fmt.Printf("传输数据,消耗字节: %d, 总计字节: %d, %d%%.\n", // event.ConsumedBytes, listener.FileSize, event.ConsumedBytes*100/listener.FileSize) //} case oss.TransferCompletedEvent: fmt.Printf("\n传输已完成,已用字节数: %d, 总计字节: %d.\n", event.ConsumedBytes, listener.FileSize) case oss.TransferFailedEvent: fmt.Printf("\n传输失败,已用字节数: %d, 总计字节: %d.\n", event.ConsumedBytes, listener.FileSize) default: } }
上面的 ProgressChanged
函数是个回调函数,上传进度会实时调用,然后去更新 ConsumedBytes
的值.
那么问题来了。当我调用 GetProgress
的时候,就会把将 OssProgressListener
结构体信息返回出去,由于是api形式,框架底层会将结构体解析成 json
然后返回给浏览器。 那么解析 json
的时候底层还是会去读取这个结构体的值信息。造成读写并发的问题。
解决思路
首先实现抽象一个结构返回这个结构体信息加锁,因为我们写数据 ConsumedBytes
也使用到了锁机制。
image.png
完整 Progress
定义
/** * Author: JeffreyBool * Date: 2019/5/25 * Time: 19:13 * Software: GoLand */ package oss import ( "github.com/aliyun/aliyun-oss-go-sdk/oss" "fmt" "sync" "encoding/json" ) type ProgressListener interface { oss.ProgressListener SetFileSha1(string) ProgressListener SetFileSize(int64) ProgressListener SetConsumedBytes(int64) ProgressListener GetFormat() OssProgressListener } // 定义进度条监听器。 type OssProgressListener struct { FileSha1 string `json:"file_sha1"` //file sha1 FileSize int64 `json:"file_size"` //file size ConsumedBytes int64 `json:"consumed_bytes"` //consumed bytes mutex *sync.RWMutex } //初始化进度条监听器 func NewOssProgressListener() ProgressListener { return &OssProgressListener{mutex: new(sync.RWMutex)} } // set file sha1. func (listener *OssProgressListener) SetFileSha1(value string) ProgressListener { listener.mutex.Lock() defer listener.mutex.Unlock() listener.FileSha1 = value return listener } // set file size. func (listener *OssProgressListener) SetFileSize(value int64) ProgressListener { listener.mutex.Lock() defer listener.mutex.Unlock() listener.FileSize = value return listener } // set consumed bytes. func (listener *OssProgressListener) SetConsumedBytes(value int64) ProgressListener { listener.mutex.Lock() defer listener.mutex.Unlock() listener.ConsumedBytes = value return listener } //获取数据 //只能为了防止 json 序列化再次读取这个值和写冲突,就使用值拷贝的方式。 func (listener *OssProgressListener) GetFormat() OssProgressListener { listener.mutex.RLock() defer listener.mutex.RUnlock() //bytes, _ := listener.Marshal() return *listener } //json 序列化加锁..防止数据冲突 func (listener *OssProgressListener) Marshal() ([]byte, error) { listener.mutex.RLock() defer listener.mutex.RUnlock() return json.Marshal(listener) } // 定义进度变更事件处理函数。 func (listener *OssProgressListener) ProgressChanged(event *oss.ProgressEvent) { listener.SetConsumedBytes(event.ConsumedBytes) //pretty.Printf("event: %# v\n", event) //pretty.Printf("listener: %# v\n", listener) switch event.EventType { case oss.TransferStartedEvent: fmt.Printf("传输已启动,已用字节数: %d, 总计字节: %d.\n", event.ConsumedBytes, listener.FileSize) case oss.TransferDataEvent: //if event.ConsumedBytes == 0 || listener.FileSize == 0 { // fmt.Printf("传输数据,消耗字节: %d, 总计字节: %d, %d%%.\n", // event.ConsumedBytes, listener.FileSize, event.ConsumedBytes) //} else { // fmt.Printf("传输数据,消耗字节: %d, 总计字节: %d, %d%%.\n", // event.ConsumedBytes, listener.FileSize, event.ConsumedBytes*100/listener.FileSize) //} case oss.TransferCompletedEvent: fmt.Printf("\n传输已完成,已用字节数: %d, 总计字节: %d.\n", event.ConsumedBytes, listener.FileSize) case oss.TransferFailedEvent: fmt.Printf("\n传输失败,已用字节数: %d, 总计字节: %d.\n", event.ConsumedBytes, listener.FileSize) default: } }
上面的 GetFormat
就是我们对外暴露的信息。 注意 读取的时候 加锁 .然后我们需要返回这个结构体的值传递类型,一定不要返回指针类型。默认值传递类型会将数据拷贝一份返回出去,这样外面拿到的数据就不是同一个变量地址的数据啦。这样做 json 解析的时候就不会发生数据冲突的问题了。
image.png
数据冲突
image.png
上图就是造成数据冲突的原因.
需要查看数据冲突命令 -race
go run -race main.go
致谢
感谢你花时间阅读,如果觉得作者写的可以,可以将本站分享给更多的人。写的不好别喷哈,小弟水平有限~~
原文链接: https://www.zhanggaoyuan.com/article/18
原文标题:[golang指针类型引起的神奇bug]
本站使用「 署名-非商业性使用 4.0 国际 (CC BY-NC 4.0) 」创作共享协议,转载或使用请署名并注明出处。
以上所述就是小编给大家介绍的《golang 指针类型引起的神奇 bug》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。