Golang线程模型比较

huangapple go评论143阅读模式
英文:

golang threading model comparison

问题

我有一段数据

type data struct {
    // 所有好的数据在这里
    ...
}

这个数据由一个管理器拥有,并且被其他线程只用于读取。管理器需要定期更新数据。我该如何设计这个线程模型?我能想到两个选项:

type manager struct {
    // 当其他线程读取数据时获取读锁。
    // 当管理器想要更新时获取写锁。
    lock sync.RWMutex
    // 一个指向数据的指针
    p *data
}
type manager struct {
    // 当其他线程想要使用数据时,复制指针。
    // 当管理器更新时,只需将p指向新数据。
    p *data
}

第二种方法可行吗?看起来我不需要任何锁。如果其他线程得到指向旧数据的指针,如果管理器更新原始指针,那么一切都没问题。由于Go语言会进行垃圾回收,当所有其他线程读取旧数据后,它将自动释放。我理解得对吗?

英文:

I have a piece of data

type data struct {
    // all good data here
    ...
}

This data is owned by a manager and used by other threads for reading only. The manager needs to periodically update the data. How do I design the threading model for this? I can think of two options:

1.

type manager struct {
    // acquire read lock when other threads read the data.
    // acquire write lock when manager wants to update.
    lock sync.RWMutex
    // a pointer holding a pointer to the data
    p *data
}
type manager struct {
    // copy the pointer when other threads want to use the data.
    // When manager updates, just change p to point to the new data.
    p *data
}

Does the second approach work? It seems I don't need any lock. If other threads get a pointer pointing to the old data, it would be fine if manager updates the original pointer. As GoLang will do GC, after all other threads read the old data it will be auto released. Am I correct?

答案1

得分: 1

你的第一个选项很好,也许是最简单的方法。然而,它可能会导致性能下降,因为它可能难以获得写锁。

正如你的问题的评论所指出的,你的第二个选项(原样)可能会导致竞态条件,并导致不可预测的行为。

你可以通过使用atomic.Value来实现你的第二个选项。这将允许你存储指向某个数据结构的指针,并原子地更新它供下一个读取者使用。例如:

// 与读取者共享的数据
type data struct {
// 所有字段
}

// 管理器
type manager struct {
v atomic.Value
}

// 读取者用来获取最新数据副本的方法,例如在循环内部使用
func (m *manager) Data() *data {
return m.v.Load().(*data)
}

// 内部方法,用于为读取者设置新数据
func (m *manager) update() {
d := &data{
// ... 在这里设置值
}
m.v.Store(d)
}

英文:

Your first option is fine and perhaps simplest to do. However, it could lead to poor performance with many readers as it could struggle to obtain a write lock.

As the comments on your question have stated, your second option (as-is) can cause a race condition and lead to unpredictable behaviour.

You could implement your second option by using atomic.Value. This would allow you to store the pointer to some data struct and atomically update this for the next readers to use. For example:

// Data shared with readers
type data struct {
   // all the fields
}

// Manager
type manager struct {
	v atomic.Value
}

// Method used by readers to obtain a fresh copy of data to 
// work with, e.g. inside loop
func (m *manager) Data() *data {
	return m.v.Load().(*data)
}

// Internal method called to set new data for readers
func (m *manager) update() {
	d:=&data{
	    // ... set values here
    }
	m.v.Store(d)
}

huangapple
  • 本文由 发表于 2017年7月25日 02:14:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/45287348.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定