根据维基百科,竞态条件被定义为电子、软件或其他系统的条件,其中系统的实质性行为取决于其他不可控事件的顺序或时间。竞争条件属于“并发”部门。并发是同时在多个任务上取得进展的艺术。让我们了解并发实际上意味着什么。
考虑以下场景以更好地理解并发。想象一个装满 laddoos 的篮子和站在篮子附近的两个人。一个人被分配了一项任务来测量篮子里每个 laddu 的数量、质量、重量并检查其他重要特征。现在他一开始观察laddoos并得出一些结论,同时第二个人篡改一些laddoos形状,将它们打成粉末,甚至吃了一些laddoos。它同时发生:一个注意到 laddoos 的特征,另一个破坏了原始特征。现在世界上没有办法让第一个知道原始数据,因为他只注意到甚至他都不知道的篡改数据。这是一个竞争条件。同时执行的两个任务会干扰一些常见的事情,导致大量数据丢失。
让我们实际编码和理解竞争条件!
package main
import (
"fmt"
"time"
)
func execute(some string) {
// initializing a infinite for loop
for i := 1; true; i++ {
// prints string and number
fmt.Println(some, i)
// this makes the program sleep for
// 100 milliseconds, wiz 10 seconds
time.Sleep(time.Millisecond * 100)
}
}
func main() {
// Simple go synchronous program
// without any concurrency
execute("First")
// when this func is called it executes
// and then passes on to next line
execute("Second")
// after the first second is to be executed
// the problem is, execute function will
// never finish execution, its trapped
// in an infinite loop so the program will
// never move to second execution.
fmt.Println("program ends successfully")
// if I'm wrong and both first and second
// execute, then this line should print too
// check the output
}
如果您使用 Windows,您很可能会在屏幕上看到这样的输出。上面的输出就这样继续下去并且没有结束,除非你手动停止它或者它用完了系统内存来执行。
考虑第二个代码。
package main
import (
"fmt"
"time"
)
func execute(some string) {
// initializing a infinite for loop
for i := 1; true; i++ {
// prints string and number
fmt.Println(some, i)
// this makes the program sleep for
// 100 milliseconds, wiz 10 seconds
time.Sleep(time.Millisecond * 100)
}
}
func main() {
// Simple go program with concurrency
go execute("First")
// Placing the go command infront of the
// func call simply creates a goroutine
execute("Second")
// The goroutine ensures that both functions
// execute simultaneously & successfully
fmt.Println("program ends successfully")
// This statement still won't execute because
// the func call is stuck in an infinite loop
// check the output
}
如果您使用 Windows,您很可能会在屏幕上看到这样的输出。当你在 Go 中创建一个 goroutine 时,它通常会通过将等待时间分配给其他主要任务来加快执行速度,现在让我们看看如果你在同一个程序中创建两个 goroutine 会发生什么。
package main
import (
"fmt"
"time"
)
func execute(some string) {
// initializing a infinite for loop
for i := 1; true; i++ {
// prints string and number
fmt.Println(some, i)
// this makes the program sleep for
// 100 milliseconds, wiz 10 seconds
time.Sleep(time.Millisecond * 100)
}
}
func main() {
// Simple go program with concurrency
go execute("First")
// Placing the go command in front of the
// func call simply creates a goroutine
go execute("Second")
// The second goroutine, you may think that the
// program will now run with lightning speed
// But, both goroutines go to the background
// and result in no output at all Because the
// program exits after the main goroutine
fmt.Println("Program ends successfully")
// This statement will now be executed
// and nothing else will be executed
// check the output
}
如果您使用 Windows,您很可能会在屏幕上看到这样的输出。让我们考虑一个竞争条件场景来结束我们的话题:
package main
// one goroutine is the main
// goroutine that comes by default
import (
"fmt"
"runtime"
"sync"
)
var wgIns sync.WaitGroup
func main() {
// shared variable
counter := 0
// the other 10 goroutines are
// supposed to come from here
wgIns.Add(10)
for i := 0; i < 10; i++ {
// goroutines are made
go func() {
for j := 0; j < 10; j++ {
// shared variable execution
counter += 1
// 100 should be the counter value but
// it may be equal to 100 or lesser
// due to race condition
}
wgIns.Done()
}()
}
// this value should actually be 11
fmt.Println("The number of goroutines before wait = ", runtime.NumGoroutine())
wgIns.Wait()
// value should be 100
fmt.Println("Counter value = ", counter)
fmt.Println("The number of goroutines after wait = ", runtime.NumGoroutine())
// this value is supposed to be 1
// but lets see if these values
// stay consistently same every
// time we run the code
}
这种不一致的发生是由于竞争条件。简单来说,比赛条件可以解释为,你有一颗糖果,两个孩子跑到你面前声称他们都饿了。他们最终都为那块巧克力而战,他们竞相抢糖果。这是一个竞争条件。这里的解决方案是:再拿一颗糖果,让两人和平地享用糖果。同样,我们可以增加资源分配以确保不发生竞争条件。