This is a creation in Article, where the information may have evolved or changed.
-
- Process pictures in parallel and wait for the end of all picture processing
- Limit concurrent Quantities
- Multiplexing Select
- Synconce
- Workaround one using mutex lock
- Solution two use a read-write lock
- Workaround three Use Synconce
Process pictures in parallel and wait for the end of all picture processing
funcMAKETHUMBNAILS6 (Filenames <-Chan string)Int64{Sizes: = Make(Chan Int64)varWG Sync. Waitgroup//number of working Goroutines forF: =RangeFilenames {WG. Add(1)//Worker Go func(fstring) {deferWg. Done () thumb, err: = thumbnail. ImageFile (f)ifErr! =Nil{log. PRINTLN (ERR)return} info, _: = OS. Stat (thumb)//OK to ignore errorSizes <-Info. Size ()} (f)}//Closer Go func() {WG. Wait ()Close(sizes)} ()varTotalInt64 forSize: =RangeSizes {Total + = size}returnTotal
This code is var wg sync.WaitGroup
used to implement waiting for the end of all picture processing.
function makeThumbnails6
is executed to
forsize := range sizes {
When it is blocked until
gofunc() { wg.Wait() close(sizes) }()
Executed to close(sizes)
. The Goroutine first blocks on wg.Wait()
until all of the picture processing is finished and the image processing is finished at the end of the call wg.Done()
.
Limit concurrent Quantities
The simplest is achieved through a channel with a cache
As below, the call tokens <- struct{}{}
gets a quota before execution, and the release quota is called after execution ends <-tokens
.
//tokens is a counting semaphore used to< /span>//enforce a limit of concurrent requests. var tokens = make ( chan struct {}, ) func crawl (url string ) []string {fmt. Println (URL) tokens <-struct {} {} //acquire a token< /span> list, err: = links. Extract (URL) <-tokens //release the token if Err! = nil {log. Print (Err)} return list}
Multiplexing Select
time.Tick
Create a channel that writes data to the channel every once in a while.
func main() { fmt.Println("Commencing countdown.") ticktime.Tick(1time.Second) for100; countdown-- { fmt.Println(countdown) <-tick } launch()}
Use when waiting for multiple channelsselect
.........}
Select blocks until a channel is available and, if more than one is available, randomly selects one.
time.After
Generates a channel after a specified time and produces a goroutine that writes a value to the channel.
func main() { // ...create abort channel... fmt.Println("Commencing countdown. Press return to abort.") select { case <-time.After(10 * time.Second): // Do nothing. case <-abort: fmt.Println("Launch aborted!") return } launch()}
Timers are commonly used in the following ways:
ticker := time.NewTicker(1from the ticker's channelticker.Stop() // cause the ticker's goroutine to terminate
Non-blocking mode
select { case <-abort: fmt.Printf("Launch aborted!\n") return default: // do nothing}
The 0 value of the channel is nil. Read and write on nil channels will always block. Case in Select
Mutual exclusion Lock
var ( mu // guards balance int)funcint) { mu.Lock() balance = balance + amount mu.Unlock()}funcint { mu.Lock() defer mu.Unlock() return balance}
This article also gives an example, if the account balance is insufficient, it will be back to the operation, Deposit(-amount)
indicating the purchase operation, to determine the Balance()
balance is insufficient:
// NOTE: not atomic!funcintbool { Deposit(-amount) if Balance() < 0 { Deposit(amount) returnfalse// insufficient funds } returntrue}
The problem with this code is that if a has 100 yuan, want to buy 300 of the car, Deposit(-amount)
resulting Balance()
in negative, then if Deposit(amount)
before a to buy less than 100 yuan of daily necessities can not buy, because at this time balance () is still negative, has not recovered.
So to buy and restore as an atomic operation.
funcWithdraw (Amountint)BOOL{mu. Lock ()deferMu. Unlock () deposit (-amount)ifBalance <0{Deposit (amount)return false //Insufficient funds}return true}funcDeposit (Amountint) {mu. Lock ()deferMu. Unlock () Deposit (amount)}funcBalance ()int{mu. Lock ()deferMu. Unlock ()returnBalance//This function requires that the lock is held.funcDeposit (Amountint) {balance + = amount}
Sync. Once
First, let's look at the following code
varIconsMap[string]image. ImagefuncLoadicons () {icons =Map[string]image. image{"Spades.png": LoadIcon ("Spades.png"),"Hearts.png": LoadIcon ("Hearts.png"),"Diamonds.png": LoadIcon ("Diamonds.png"),"Clubs.png": LoadIcon ("Clubs.png"), }}//Note:not concurrency-safe!funcIcon (namestring) image. Image {ificons = =Nil{loadicons ()//One-time initialization}returnIcons[name]}
Where Icon
the function is not concurrency-safe , if there are two Goroutines A and b,a first executed to if icons == nil
then switch to b,b at this time the judgment if icons == nil
is still true, so loadIcons
it executes two times.
Workaround one, use mutual exclusion locks:
var// guards icons// Concurrency-safe.funcstring) image.Image { mu.Lock() defer mu.Unlock() ifnil { loadIcons() } return icons[name]}
However, the problem is that after icons is initialized, subsequent read operations cannot be performed in parallel.
Solution two use a read-write lock
var mu sync. Rwmutex //guards icons var icons map [string ]image. Image//concurrency-safe. func Icon (name string ) image. Image {mu. Rlock () if icons! = nil {icon: = Icons[nam E] Mu. Runlock () return icon} mu. Runlock () //acquire an exclusive lock Mu. Lock () if icons = nil {//note:must recheck for nil loadicons ()} icon: = Icons[name] mu. Unlock () return icon}
The problem is that it costs too much.
Workaround three use sync. Once
var loadIconsOnce sync.Oncevarmap[string]image.Image// Concurrency-safe.funcstring) image.Image { loadIconsOnce.Do(loadIcons) return icons[name]}