并发限制与信号量,为什么我必须先释放缓冲区?

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

Concurrency Limiting with Semaphore, Why do I have to free up the buffer first?

问题

为什么只有在<-sem之前的results<-msg时,这段代码才能正常工作?我以为<-sem会释放缓冲区以供新的子例程使用。除此之外,有没有更好的方法来从goroutine中返回函数的结果?

从逻辑上讲,我想这样做,但是除非<-sem在results<-msg之前,否则它不起作用。

以下是你的代码:

package main

import (
	"fmt"
	"strconv"
	"time"
	"math/rand"
)

var intSlice = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}

func printer(account int) string {
	n := rand.Intn(1)
	fmt.Printf("Sleeping %d seconds...\n", n)
	time.Sleep(time.Duration(n)*time.Second)
	return "Account Done " + strconv.Itoa(account)
}

func main() {
	threads := 2

	results := make(chan string, threads)

	sem := make(chan bool, threads)

	for _, account := range intSlice {
		sem <- true //block

		go func(account int) {
			var msg = printer(account)
			defer func() { <-sem; results<-msg }()
		}(account)
	}

	for i := 0; i < cap(sem); i++ {
		sem <- true
	}

	for i := 0; i < len(intSlice); i++ {
		select {
		case msg1 := <-results:
			fmt.Println("received", msg1)
		}
	}

}

请注意,我只翻译了你提供的代码部分,其他部分保持原样。

英文:

Why will this code only work if <-sem; before results<-msg;? I thought <-sem frees up the buffer for a new subroutine. Apart from that is there a better way to return the result from a function within a go routine?

Logically I want to do this, but it doesnt work unless <-sem is above results<-msg.

var msg = printer(account)
results&lt;-msg;
&lt;-sem;

Here's my code:

package main

import (
	&quot;fmt&quot;
	&quot;strconv&quot;
	&quot;time&quot;
	//&quot;runtime&quot;
	&quot;math/rand&quot;
)

var intSlice = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}

func printer(account int) string {
	 n := rand.Intn(1) 
    fmt.Printf(&quot;Sleeping %d seconds...\n&quot;, n)
    time.Sleep(time.Duration(n)*time.Second)
	return &quot;Account Done &quot; + strconv.Itoa(account) 
}

func main() {
    threads := 2
	
    results := make(chan string, threads)
   
    sem := make(chan bool, threads)

    for _, account := range intSlice {
        sem &lt;- true //block
		
        go func(account int) {
			var msg = printer(account)
            defer func() { &lt;-sem; results&lt;-msg; }()
        }(account)
    }
	
    for i := 0; i &lt; cap(sem); i++ {
        sem &lt;- true
    }
	
    for i := 0; i &lt; len(intSlice); i++ {
        select {
        case msg1 := &lt;-results:
            fmt.Println(&quot;received&quot;, msg1)
        }
	}

}

答案1

得分: 2

所以,sem通道使用长度为2的缓冲区进行初始化。

这使得程序可以在阻塞之前在该通道中推送两次,即在sem <- true //block之前。

要从通道中释放一个空间,你需要在完成作业后使用<-sem读取一个值,这意味着现在其他人可以再次推送到通道并在sem <- true //block中继续执行。

从逻辑上讲,我想这样做,但是除非<-semresults<-msg之上,否则它不起作用。

var msg = printer(account)
results<-msg;
<-sem;

你不能这样写,因为你在results<-msg处阻塞。由于results尚未被读取,没有任何东西可以使这些指令继续执行。

实际上发生的是,在单独的例程中,你在results<-msg处积累了多达len(sem)个例程,它们在len(results)处阻塞。它们将在达到循环for i := 0; i < len(intSlice); i++ {时解开,但是它们不会解开,因为mainsem <- true //block处阻塞,等待那些在堆栈上的例程使用<-sem释放空间。

英文:

so, the sem channel is initialized with a buffer of len=2

This allows for the program to push twice in this channel, before blocking in sem &lt;- true //block.

To free a room from the channel, you read a value with &lt;-sem once a job is done, which means that by now, someone else can push again to the channel and move on in sem &lt;- true //block.

> Logically I want to do this, but it doesnt work unless <-sem is above
> results<-msg.
>
> var msg = printer(account)
> results<-msg;
> <-sem;

you can not write this because you are blocking at results&lt;-msg. As results is not yet consumed by a reader, nothing allows for those instructions to move forward.

what happens really is that you are accumulating up to len(sem) routines on the stack blocking on up to len(results) at results &lt;- msg in a separate routine.

They would unwind when you reach that loop for i := 0; i &lt; len(intSlice); i++ {, but they don't because main blocks at sem &lt;- true //block waiting for those on-stack routines to free room using &lt;-sem.

huangapple
  • 本文由 发表于 2021年8月15日 01:40:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/68785668.html
匿名

发表评论

匿名网友

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

确定