Last active
April 19, 2020 20:55
-
-
Save igorzakhar/a04c799881c5b585f7b9eb943edf117b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"bufio" | |
"bytes" | |
"fmt" | |
"io/ioutil" | |
"net/http" | |
"os" | |
"sync" | |
) | |
// структура для хранения результатов каждого запроса | |
// включая кол-во вхождений строки "Go" в теле ответа | |
type result struct { | |
url string | |
count int | |
err error | |
} | |
func main() { | |
maxGoroutines := 5 | |
resultChannel := make(chan *result) | |
done := make(chan bool) | |
go RunConcurrentTasks(done, resultChannel, maxGoroutines) | |
// блокируем закрыте канала до окончания работы горутины RunConcurrentTasks, | |
// закрываем канал resultChannel для остановки цикла чтения из канала в функции OutputOfResults | |
go func() { | |
<-done | |
close(resultChannel) | |
}() | |
OutputOfResults(resultChannel) | |
} | |
// RunConcurrentTasks запускает параллельные задачи GetWordCount, число одновременно | |
// выполняемыx задач ограничено величиной передаваемой в аргументе concurrencyLimit | |
func RunConcurrentTasks(done chan bool, resultChan chan *result, concurrencyLimit int) { | |
var waitgroup sync.WaitGroup | |
// Буферизированный канал используется как семафор, размер канала | |
// определяет количество задач, которые могут обрабатываться одновременно | |
semaphoreChan := make(chan struct{}, concurrencyLimit) | |
defer func() { | |
close(semaphoreChan) | |
}() | |
scanner := bufio.NewScanner(os.Stdin) | |
for scanner.Scan() { | |
// отправляем пустую структуру в канал semaphoreChan, при заполнении | |
// буфера блокируется пока один из получателей не прочитает значение из канала | |
semaphoreChan <- struct{}{} | |
waitgroup.Add(1) | |
go func(url string) { | |
defer waitgroup.Done() | |
res := GetWordCount(url) | |
resultChan <- &res // отправляем результат который будет получен в функции OutputOfResults | |
<-semaphoreChan // читаем из канала, освобождая буфер позволяем запускатся следующей горутине | |
}(scanner.Text()) | |
} | |
waitgroup.Wait() | |
done <- true | |
} | |
// GetWordCount отправляет http get запрос на указанный url-адрес и считает | |
// кол-во вхождений строки "Go" в теле ответа, возвращает значение типа result | |
func GetWordCount(url string) result { | |
response, err := http.Get(url) | |
if err != nil { | |
return result{url, 0, err} | |
} | |
defer response.Body.Close() | |
body, err := ioutil.ReadAll(response.Body) | |
if err != nil { | |
return result{url, 0, err} | |
} | |
wordCount := bytes.Count(body, []byte("Go")) // считаем сразу без приведения к типу string | |
return result{url, wordCount, err} | |
} | |
// OutputOfResults слушает канал resultChan, при получении результата вычислений каждой задачи | |
// GetWordCount выводит его в stdout, аккумулирует значение поля count экземпляра структуры result | |
// в переменной totalCount и по окончании цикла чтения из канала выводит его в stdout | |
func OutputOfResults(resultChan chan *result) { | |
totalCount := 0 | |
for res := range resultChan { | |
if res.err != nil { | |
continue | |
} | |
fmt.Printf("Count for %s: %d\n", res.url, res.count) | |
totalCount += res.count | |
} | |
fmt.Printf("Total: %v\n", totalCount) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment