Ho tentato di dare una svolta alla concorrenza in Golang rifattorizzando uno dei miei programmi di utilità da linea di comando negli ultimi giorni, ma sono bloccato.Perché il mio canale Golang scrive blocco per sempre?
Here's codice originale (ramo principale).
Here's il ramo con concomitanza (x_concorrente).
Quando eseguo il codice in concomitanza con go run jira_open_comment_emailer.go
, il defer wg.Done()
non esegue se il problema JIRA viene aggiunto al canale here, che fa sì che il mio wg.Wait()
da appendere per sempre.
L'idea è che ho una grande quantità di problemi JIRA e voglio eseguire una goroutine per ciascuno per vedere se ha un commento a cui devo rispondere. Se lo fa, voglio aggiungerlo ad una struttura (ho scelto un canale dopo qualche ricerca) che posso leggere da una coda in seguito per creare un promemoria via email.
Ecco la relativa sezione del codice:
// Given an issue, determine if it has an open comment
// Returns true if there is an open comment on the issue, otherwise false
func getAndProcessComments(issue Issue, channel chan<- Issue, wg *sync.WaitGroup) {
// Decrement the wait counter when the function returns
defer wg.Done()
needsReply := false
// Loop over the comments in the issue
for _, comment := range issue.Fields.Comment.Comments {
commentMatched, err := regexp.MatchString("~"+config.JIRAUsername, comment.Body)
checkError("Failed to regex match against comment body", err)
if commentMatched {
needsReply = true
}
if comment.Author.Name == config.JIRAUsername {
needsReply = false
}
}
// Only add the issue to the channel if it needs a reply
if needsReply == true {
// This never allows the defered wg.Done() to execute?
channel <- issue
}
}
func main() {
start := time.Now()
// This retrieves all issues in a search from JIRA
allIssues := getFullIssueList()
// Initialize a wait group
var wg sync.WaitGroup
// Set the number of waits to the number of issues to process
wg.Add(len(allIssues))
// Create a channel to store issues that need a reply
channel := make(chan Issue)
for _, issue := range allIssues {
go getAndProcessComments(issue, channel, &wg)
}
// Block until all of my goroutines have processed their issues.
wg.Wait()
// Only send an email if the channel has one or more issues
if len(channel) > 0 {
sendEmail(channel)
}
fmt.Printf("Script ran in %s", time.Since(start))
}
Hai 'len (canale)' dappertutto, ma quel canale non ha lunghezza perché è senza buffer. È necessario ricevere dal canale ogni invio per completare (e in generale, prendere decisioni in base alla lunghezza di un canale memorizzato nel buffer è un errore, dal momento che le operazioni concorrenti possono correre per modificare quel valore) – JimB
Quindi, se sto facendo tutto delle mie scritture sul canale, in attesa che vengano completate, e quindi leggendo dal canale ... ciò non può mai accadere perché le mandate non verranno mai effettivamente completate e innescare il 'defer wg.Done()'? Come affronteresti l'implementazione di questa concorrenza, in generale? Inoltre, non sono sicuro che tu sia corretto su 'len (canale)', poiché i godocs affermano che restituisce il numero corrente di elementi nel canale, non la capacità come 'cap (canale)' . https://golang.org/pkg/builtin/#len –
'len (canale)' restituisce il numero corrente di elementi in un canale "bufferizzato", ma poiché i canali vengono solitamente utilizzati contemporaneamente, il risultato di 'len' è "stantio" non appena lo leggi. Di solito si hanno goroutine inverse e inviate dal canale. Vorrei consigliare di passare di nuovo attraverso la sezione [Concurrency] (https://tour.golang.org/concurrency/1) nel Tour Of Go per capire meglio come funzionano i canali. – JimB