Go中的同步条件语句
Synchronized conditional statement in Go
我有一个方法可以在多个 goroutine 中同时使用 运行。
在这个方法中,我有一个条件语句。如果条件语句为真,我希望所有其他调用此方法的 goroutines 等待 一个且只有一个 goroutines 在继续下一节之前执行此条件语句。
例如:
type SomeClass struct {
mu sync.Mutex
}
func (c *SomeClass) SomeFunc() {
//Do some calculation
if condition {
//This part should be executed by only one goroutine if the condition is true.
//All others must wait for this to finish
}
//Additional calculations
}
我想这样使用它:
func main(){
//initilize
go someClass.SomeFunc()
//If the condition is true, the following will wait at the conditional statement until the first one finishes the code inside the conditional block
//Once it's done, they can run concurrently
go someClass.SomeFunc()
go someClass.SomeFunc()
}
编辑
这可能不是正确的设计,所以我正在寻找有关如何实现它的任何建议。
编辑2:
请注意,每个例程都有自己的条件。 condition
的值不在线程之间共享。但是,只有当 2 个或更多例程中的条件恰好同时为真时,条件内的工作才应该 运行 一次。
您需要一个互斥锁来保护条件免受并发影响 read/writes,然后是一个在您希望再次执行同步代码时重置条件的方法。
type SomeClass struct {
conditionMu sync.Mutex
condition bool
}
func (c *SomeClass) SomeFunc() {
// Lock the mutex, so that concurrent calls to SomeFunc will wait here.
c.conditionMu.Lock()
if c.condition {
// Synchronous code goes here.
// Reset the condition to false so that any waiting goroutines won't run the code inside this block again.
c.condition = false
}
// Unlock the mutex, and any waiting goroutines.
c.conditionMu.Unlock()
}
// ResetCondition sets the stored condition to true in a thread-safe manner.
func (c *SomeClass) ResetCondition() {
c.conditionMu.Lock()
c.condition = true
c.conditionMu.Unlock()
}
该题其他答案不符合题目要求,均不正确
如果将锁添加到条件语句之外,那么它将充当屏障并强制所有例程在该位置同步。这不是这个问题的重点。假设解析条件值需要很长时间,我们不想一次一个例程地检查值。我们想让每个进程同时检查条件,如果条件为假,我们可以不停地前进。
如果条件不成立,我们希望确保 goroutines 运行 并行。在方法内部和条件语句外部添加锁将不允许这种情况发生。
以下解决方案是正确的并且通过了所有测试并且表现良好。
解决方案一:
使用 2 个嵌套条件语句,例如:
注意,在这种情况下,如果条件为假,则不会调用锁,也不需要同步。一切都可以 运行 并行。
type SomeClass struct {
conditionMu sync.Mutex
rwMu sync.RWMutex
additionalWorkRequired bool
}
func (c *SomeClass) SomeFunc() {
//Do some work ...
//Note: The condition is not shared, some routines can have false and some true at the same time, which is fine.
condition := true;
// All routines can check this condition and go inside the block if the condition is true
if condition {
c.rwMutex.Lock()
c.additionalWorkRequired = true
c.rwMutex.Unlock()
//Lock so other routines can wait here for the first one
c.conditionMu.Lock()
if c.additionalWorkRequired {
// Synchronous code goes here.
c.additionalWorkRequired = false
}
//Unlock so all other processors can move forward in parallel
c.conditionMu.unlock()
}
//Finish up the remaining work
}
方案二:
使用sync/singleflight中的do
函数可以自动处理这种情况。
来自文档:
Do executes and returns the results of the given function, making sure that only one execution is in-flight for a given key at a time. If a duplicate comes in, the duplicate caller waits for the original to complete and receives the same results. The return value shared indicates whether v was given to multiple callers.
编辑:
由于许多人似乎对这个问题和答案感到困惑,我添加了一个可能会使事情更清楚的用例:
1. Send a HTTP Request
2. If the server returns an error saying credentials are incorrect (This is condition):
2.1. Save current credentials in a local variable
2.2. Acquire the mutex lock
2.2.1. Compare the shared credentials with the ones in the local variable(This is the second condition)
If they are the same, then replace them with new ones
2.3. Unlock
2.4. Retry request
我有一个方法可以在多个 goroutine 中同时使用 运行。
在这个方法中,我有一个条件语句。如果条件语句为真,我希望所有其他调用此方法的 goroutines 等待 一个且只有一个 goroutines 在继续下一节之前执行此条件语句。
例如:
type SomeClass struct {
mu sync.Mutex
}
func (c *SomeClass) SomeFunc() {
//Do some calculation
if condition {
//This part should be executed by only one goroutine if the condition is true.
//All others must wait for this to finish
}
//Additional calculations
}
我想这样使用它:
func main(){
//initilize
go someClass.SomeFunc()
//If the condition is true, the following will wait at the conditional statement until the first one finishes the code inside the conditional block
//Once it's done, they can run concurrently
go someClass.SomeFunc()
go someClass.SomeFunc()
}
编辑 这可能不是正确的设计,所以我正在寻找有关如何实现它的任何建议。
编辑2:
请注意,每个例程都有自己的条件。 condition
的值不在线程之间共享。但是,只有当 2 个或更多例程中的条件恰好同时为真时,条件内的工作才应该 运行 一次。
您需要一个互斥锁来保护条件免受并发影响 read/writes,然后是一个在您希望再次执行同步代码时重置条件的方法。
type SomeClass struct {
conditionMu sync.Mutex
condition bool
}
func (c *SomeClass) SomeFunc() {
// Lock the mutex, so that concurrent calls to SomeFunc will wait here.
c.conditionMu.Lock()
if c.condition {
// Synchronous code goes here.
// Reset the condition to false so that any waiting goroutines won't run the code inside this block again.
c.condition = false
}
// Unlock the mutex, and any waiting goroutines.
c.conditionMu.Unlock()
}
// ResetCondition sets the stored condition to true in a thread-safe manner.
func (c *SomeClass) ResetCondition() {
c.conditionMu.Lock()
c.condition = true
c.conditionMu.Unlock()
}
该题其他答案不符合题目要求,均不正确
如果将锁添加到条件语句之外,那么它将充当屏障并强制所有例程在该位置同步。这不是这个问题的重点。假设解析条件值需要很长时间,我们不想一次一个例程地检查值。我们想让每个进程同时检查条件,如果条件为假,我们可以不停地前进。
如果条件不成立,我们希望确保 goroutines 运行 并行。在方法内部和条件语句外部添加锁将不允许这种情况发生。
以下解决方案是正确的并且通过了所有测试并且表现良好。
解决方案一:
使用 2 个嵌套条件语句,例如:
注意,在这种情况下,如果条件为假,则不会调用锁,也不需要同步。一切都可以 运行 并行。
type SomeClass struct {
conditionMu sync.Mutex
rwMu sync.RWMutex
additionalWorkRequired bool
}
func (c *SomeClass) SomeFunc() {
//Do some work ...
//Note: The condition is not shared, some routines can have false and some true at the same time, which is fine.
condition := true;
// All routines can check this condition and go inside the block if the condition is true
if condition {
c.rwMutex.Lock()
c.additionalWorkRequired = true
c.rwMutex.Unlock()
//Lock so other routines can wait here for the first one
c.conditionMu.Lock()
if c.additionalWorkRequired {
// Synchronous code goes here.
c.additionalWorkRequired = false
}
//Unlock so all other processors can move forward in parallel
c.conditionMu.unlock()
}
//Finish up the remaining work
}
方案二:
使用sync/singleflight中的do
函数可以自动处理这种情况。
来自文档:
Do executes and returns the results of the given function, making sure that only one execution is in-flight for a given key at a time. If a duplicate comes in, the duplicate caller waits for the original to complete and receives the same results. The return value shared indicates whether v was given to multiple callers.
编辑:
由于许多人似乎对这个问题和答案感到困惑,我添加了一个可能会使事情更清楚的用例:
1. Send a HTTP Request
2. If the server returns an error saying credentials are incorrect (This is condition):
2.1. Save current credentials in a local variable
2.2. Acquire the mutex lock
2.2.1. Compare the shared credentials with the ones in the local variable(This is the second condition)
If they are the same, then replace them with new ones
2.3. Unlock
2.4. Retry request