Json 隐藏结构字段并使其同步访问和修改字段的最佳方法是什么?

Json 隐藏结构字段并使其同步访问和修改字段的最佳方法是什么?,json,go,struct,concurrency,mutex,Json,Go,Struct,Concurrency,Mutex,这是我在使用golang结构时面临的一个问题 type User struct { name string `json:"name"` email string `json:"email"` } 现在我希望这个结构字段的访问和修改是并发安全的 因此,我们添加了一个互斥体,并添加了锁定互斥体的方法 用户代码现在只能通过方法访问和变异,不能直接访问字段 type User struct { name string `json:"name"` emai

这是我在使用golang结构时面临的一个问题

 type User struct {
     name  string `json:"name"`
     email string `json:"email"`
 }
现在我希望这个结构字段的访问和修改是并发安全的 因此,我们添加了一个互斥体,并添加了锁定互斥体的方法 用户代码现在只能通过方法访问和变异,不能直接访问字段

type User struct {
     name string  `json:"name"`
     email string `json:"email"`
     sync.RWMutex `json:"-"`
}

func (u *User) Name() string {
   u.RLock()
   defer u.RUnlock()

   return u.name  
}

func (u *User) Email() string {
   u.RLock()
   defer u.RUnlock()

   return u.email  
}

func (u *User) SetName(p string) {
   u.Lock()
   defer u.Unlock()

   u.name = p  
}

func (u *User) SetEmail(p string) {
   u.RLock()
   defer u.RUnlock()

   u.email = p  
}
到目前为止还不错,但问题是json/bson编组失败,因为它需要导出字段

func (self User) MarshalJSON() ([]byte, error) {
    var usr struct {
        Name  string `json:"name"` 
        Email string `json:"email,omitempty"`
        sync.RWMutex `json:"-"`
    }
    return json.Marshal(usr)
}

func (self *User) UnmarshalJSON(b []byte) error {
    var usr struct {
        Name   string  `json:"name"`
        Email  string  `json:"email"` 
        sync.RWMutex   `json:"-"`
    }

    if err := json.Unmarshal(b, &usr); err != nil {
        return err
    }

    self.name = usr.Name
    self.email = usr.Email

    return nil
}
因此,我实现了自定义封送,它返回一个类似的结构,但带有导出字段

func (self User) MarshalJSON() ([]byte, error) {
    var usr struct {
        Name  string `json:"name"` 
        Email string `json:"email,omitempty"`
        sync.RWMutex `json:"-"`
    }
    return json.Marshal(usr)
}

func (self *User) UnmarshalJSON(b []byte) error {
    var usr struct {
        Name   string  `json:"name"`
        Email  string  `json:"email"` 
        sync.RWMutex   `json:"-"`
    }

    if err := json.Unmarshal(b, &usr); err != nil {
        return err
    }

    self.name = usr.Name
    self.email = usr.Email

    return nil
}
但这并不能完全保证用户结构并发的安全,因为marhsaling代码没有被锁定

我的问题是如何使编组代码使用相同的互斥? 当我们创建结构的多个实例时,将互斥锁设置为全局并不能解决问题。 封送处理中声明的用户结构与主用户结构不同,因此锁定内部结构的互斥体是没有意义的


实现这一点的最佳方法是什么?

您不必向封送的值添加互斥,这是毫无意义的

但在复制或设置其字段时,确实需要使用
用户的互斥体

一些重要的事情:

func (u *User) MarshalJSON() ([]byte, error) {
    u.RLock()
    usr := struct {
        Name  string `json:"name"`
        Email string `json:"email,omitempty"`
    }{u.name, u.email}
    u.RUnlock()

    return json.Marshal(usr)
}

func (u *User) UnmarshalJSON(b []byte) error {
    usr := struct {
        Name  string `json:"name"`
        Email string `json:"email"`
    }{}

    if err := json.Unmarshal(b, &usr); err != nil {
        return err
    }

    u.Lock()
    u.name = usr.Name
    u.email = usr.Email
    u.Unlock()

    return nil
}

  • 如果嵌入非指针互斥体,则必须使用指针接收器指定所有方法,否则将复制锁
  • 您不需要在未报告的字段上指定
    json
    标记,这是多余的。更进一步,由于您提供了自己的封送逻辑,您甚至不必提供任何
    json
    标记,因为它们根本不会被使用。所以这个
    用户
    就足够了:

    type User struct {
        name  string
        email string
        sync.RWMutex
    }
    
  • 即使
    name
    email
    未报告,这些值也不是“安全的”,因为您提供了一个导出的
    MarshalJSON()
    方法,该方法返回这些值(JSON格式)。对于访问
    User.name
    User.email
    ,您仍然具有编译时安全性,但要知道它们存储的值不是秘密的

示例:

func (u *User) MarshalJSON() ([]byte, error) {
    u.RLock()
    usr := struct {
        Name  string `json:"name"`
        Email string `json:"email,omitempty"`
    }{u.name, u.email}
    u.RUnlock()

    return json.Marshal(usr)
}

func (u *User) UnmarshalJSON(b []byte) error {
    usr := struct {
        Name  string `json:"name"`
        Email string `json:"email"`
    }{}

    if err := json.Unmarshal(b, &usr); err != nil {
        return err
    }

    u.Lock()
    u.name = usr.Name
    u.email = usr.Email
    u.Unlock()

    return nil
}

“如果嵌入非指针互斥体,则必须使用指针接收器指定所有方法,否则将复制锁!”:完成。我们有一个过梁来检查。我以一种艰难的方式了解到了这一点:“你不必向封送的值添加互斥,这是毫无意义的。”我理解你的意思是,由于结构正在初始化,因此目前没有并发访问问题。就像java构造函数不能声明为synchronized一样。感谢您确认我想得太多:-)“尽管name和email未被报告,但这些值并不“安全”,因为您提供了一个导出的MarshalJSON()方法,该方法返回这些值(JSON格式)。对于访问User.name和User.email,您仍然具有编译时安全性,但要知道它们存储的值不是秘密的。”你说得对,但在我的情况下,这并不重要。但这并不妨碍go race检测器将其标记为数据竞赛。我可以理解为什么它们是同时访问同一字段的两个函数。现在,我们不得不忽略it@rajeshnair这不应该导致数据竞争,因为每当访问
User
的字段时,它们总是受到互斥锁的保护。你不应该忽视数据竞争。上面的代码没有任何种族。