下面是使用的結構體接口抽象定義,其實就是將結構體存進一個 map
裏。因爲是讀寫都比較頻繁,我加了讀寫鎖。git
// 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 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
}
// 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
的值.github
那麼問題來了。當我調用 GetProgress
的時候,就會把將 ProgressListener
(ProgressListener
是接口:指針類型,指針傳遞) 的實現 *OssProgressListener
結構體信息返回出去,因爲是api形式,框架底層會將結構體解析成json
而後返回給瀏覽器。 那麼解析json
的時候底層仍是會去讀取這個結構體的值信息。形成讀寫併發的問題。golang
首先實現抽象一個結構返回這個結構體信息加鎖,由於咱們寫數據 ConsumedBytes
也使用到了鎖機制。json
Progress
定義//get progress listener.
func (upload *UploaderGateway) GetProgress(key string) (v *OssProgressListener, 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
}
複製代碼
這裏 progressListener
接受到的是值傳遞,因此外面修改不會對 ProgressListener
形成影響api
/** * 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 解析的時候就不會發生數據衝突的問題了。瀏覽器
上圖就是形成數據衝突的緣由.併發
須要查看數據衝突命令 -race
框架
go run -race main.go函數
感謝你花時間閱讀,若是以爲做者寫的能夠,能夠將本站分享給更多的人。寫的很差別噴哈,小弟水平有限~~ 🤣🤣🤣ui
原文連接:www.zhanggaoyuan.com/article/18
原文標題:[golang指針類型引發的神奇bug]
本站使用「 署名-非商業性使用 4.0 國際 (CC BY-NC 4.0)」創做共享協議,轉載或使用請署名並註明出處。