Sono solo un principiante e il mio libro non tratta questo argomento. Ho studiato il mio problema e ho scoperto che un'implementazione del modello consumatore-produttore è la soluzione ideale e l'ho googolata, ho letto quello che potevo, ho tentato di enunciare esempi ... ma non sono stato fortunato. Gradirei davvero un po 'di orientamento.
Sto scrivendo in Ruby, se questo fa la differenza.
Sfondo
Sto scrivendo uno script che scrobbling il mio backlog della libreria musicale su Last.FM. Per chiunque non abbia familiarità con quel servizio, ciò significa semplicemente che sto facendo molte richieste POST a un'API HTTP.
Comincio con un array, ogni elemento è un hash / dizionario come {artist: "The Cure", track: "Siamese Twins"}
. Eseguo le chiamate eseguendo un'iterazione sull'array e inviando la semplice chiamata di metodo lastfm.track.scrobble artist: song[:artist], track: song[:track]
.
Problema
Fare questo in uno stile di blocco alla volta semplice funziona perfettamente, ma è molto molto lento, perché sto aspettando ~ 2 secondi per ogni richiesta HTTP di viaggiare per il mondo e ritornare. Potrei finire 5 volte più velocemente se avessi inviato contemporaneamente 5 richieste HTTP.
Ho chiesto su Stack Overflow quale sarebbe stata la soluzione migliore. Dividi l'array in cinque parti e dai a parte un thread che esegue una richiesta alla volta? Qualcosa come XMLHttpRequest
di JavaScript che ha un ciclo di eventi e una funzione di callback? Mi hanno detto che questa scelta sarebbe più adatta ai programmatori SE, e che probabilmente desidero il modello del produttore-consumatore. L'ho cercato e sembra il tipo di cosa di cui ho bisogno.
Il mio tentativo in Ruby, basato su un post di StackOverflow
# "lastfm" is an object representing my Last.FM profile,
# its .track.scrobble method handles a POST request and waits until it returns
# "songs" is an array of hashes like {artist: "The Cure", track: "One Hundred Years"}
queue = SizedQueue.new(10) # this will only allow 10 items on the queue at once
p1 = Thread.new do
songs.each do |song|
scrobble = lastfm.track.scrobble artist: song[:artist], track: song[:track]
puts "Scrobbled #{song[:track]} by #{song[:artist]}."
queue << scrobble
end
queue << "done"
end
consumer = Thread.new do
blocker = queue.pop(true) # don't block when zero items are in queue
Thread.exit if blocker == "done"
process(blocker)
end
# wait for the consumer to finish
consumer.join
Il mio errore
Errore nella chiamata al metodo "pop" con errore "coda vuota".
Non ne so abbastanza di questa roba per capire veramente cosa sta succedendo. Sembra che un thread stia compilando una coda con le chiamate API da eseguire, mai più di 10 alla volta, mentre un altro thread sta consumando da quella coda e sta eseguendo. Ma perché pop? Dove viene mai effettivamente eseguito? Perché aggiungo "completato" alla coda?
Sono preoccupato per la riga scrobble = lastfm.track.scrobble artist: song[:artist], track: song[:track]
. Questo non chiamerà il metodo lastfm.track.scrobble a titolo definitivo e memorizzerà il suo valore di ritorno come scrobble
? Dovrei usare invece proc o lambda e chiamare quel proc / lambda nel consumatore?