LINUX.ORG.RU

Многопоточность

 


0

1
package main

import (
	"bufio"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
	"time"
)

var url_list string = ""

func init() {
	flag.StringVar(&url_list, "i", url_list, "")
	flag.Parse()
}

func download(url string) {
	t := time.Now()
	resp, err := http.Get(url)
	if err != nil {
		fmt.Println(err)
	}
	defer resp.Body.Close()
	content, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println(err)
	}
	fname := strings.Split(url, "/")
	file, err := os.Create(fname[len(fname)-1])
	if err != nil {
		fmt.Println(err)
	}
	file.Write(content)
	defer file.Close()
	fmt.Printf("Filename: %s\nDownload time: %s\n\n", fname[len(fname)-1], time.Since(t))
}

func main() {
	ll, err := os.Open(url_list)
	if err != nil {
		fmt.Println(err)
	}
	defer ll.Close()
	scanner := bufio.NewScanner(ll)
	for scanner.Scan() {
		download(scanner.Text())
	}
	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}
}

Когда я пытаюсь использовать goroutine

...
for scanner.Scan() {
    go download(scanner.Text())
...

то программа заканчивается сразу после запуска. Или поделитесь ссылкой, где описывается понятно для дебила как эти штуки работают. Спасибо.

★★

Наверное, самое правильно передавать в download канал, куда складывать результат, и потом из него читать сколько нужно.

mix_mix ★★★★★ ()

видимо, программа завершается тогда, когда завершается main, а не когда завершаются все потоки. Нужно как-то не дать main завершиться раньше. Помимо каналов, можно использовать WaitGroup из пакета sync. Там даже есть весьма похожий пример:

    var wg sync.WaitGroup
    var urls = []string{
            "http://www.golang.org/",
            "http://www.google.com/",
            "http://www.somestupidname.com/",
    }
    for _, url := range urls {
            // Increment the WaitGroup counter.
            wg.Add(1)
            // Launch a goroutine to fetch the URL.
            go func(url string) {
                    // Decrement the counter when the goroutine completes.
                    defer wg.Done()
                    // Fetch the URL.
                    http.Get(url)
            }(url)
    }
    // Wait for all HTTP fetches to complete.
    wg.Wait()

sevenredlines ()
Ответ на: комментарий от dnb

в WaitGroup есть счётчик. Изначально он равен 0.

func (wg *WaitGroup) Add(delta int) добавляет к счётчику delta.

func (wg *WaitGroup) Done() уменьшает счётчик на 1.

func (*WaitGroup) Wait() блокирует текущий поток до тех пор, пока счётчик не станет равным 0.

см. пример выше - перед запуском каждого потока wg.Add(1), когда поток завершается, он делает wg.Done(). Главный поток запускает все остальные и делает wg.Wait(), чтобы дождаться их завершения.

sevenredlines ()
Ответ на: комментарий от dnb

Горутины это не треды. Ты реквестируешь скачивание, навешиваешь колбек, и ... выходишь. Почему нет?

Как правильно ждать тут уже написали.

redixin ★★★★ ()

Делать flag.Parse() в init() - моветон.

Алсо, на швабре пролетала статья с разработкой менеджера закачек на Go.

BigAlex ★★★ ()

Переписал используя sync

package main

import (
	"bufio"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
	"sync"
	"time"
)

var url_list string = ""

func init() {
	flag.StringVar(&url_list, "i", url_list, "")
	flag.Parse()
}

func download(url string) {
	t := time.Now()
	resp, err := http.Get(url)
	if err != nil {
		fmt.Println(err)
	}
	defer resp.Body.Close()
	content, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println(err)
	}
	fname := strings.Split(url, "/")
	file, err := os.Create(fname[len(fname)-1])
	if err != nil {
		fmt.Println(err)
	}
	file.Write(content)
	defer file.Close()
	fmt.Printf("Filename: %s\n Download time: %s\n\n", fname[len(fname)-1], time.Since(t))
}

func main() {
	var wg sync.WaitGroup
	ll, err := os.Open(url_list)
	if err != nil {
		fmt.Println(err)
	}
	defer ll.Close()
	scanner := bufio.NewScanner(ll)
	for scanner.Scan() {
		wg.Add(1)
		//download(scanner.Text())
		go func(url string) {
			defer wg.Done()
			download(url)
		}(scanner.Text())
	}
	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}
}

При запуске куча ошибок вида

goroutine 182 [runnable]:
main.main.func1(0x1860e900, 0x186b6e80, 0x38)
	/home/alex/go/example.go:55
created by main.main
	/home/alex/go/example.go:58 +0x264

Теперь буду пробовать с каналами.

dnb ★★ ()
Ответ на: комментарий от BigAlex

Мне не столько менеджер закачек нужен, сколько понять как работает goroutine.

dnb ★★ ()
Ответ на: комментарий от Gvidon

Ой, не с того окна скопировал.

package main

import (
	"bufio"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
	"sync"
	"time"
)

var url_list string = ""

func init() {
	flag.StringVar(&url_list, "i", url_list, "")
	flag.Parse()
}

func download(url string) {
	t := time.Now()
	resp, err := http.Get(url)
	if err != nil {
		fmt.Println(err)
	}
	defer resp.Body.Close()
	content, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println(err)
	}
	fname := strings.Split(url, "/")
	file, err := os.Create(fname[len(fname)-1])
	if err != nil {
		fmt.Println(err)
	}
	file.Write(content)
	defer file.Close()
	fmt.Printf("Filename: %s\n Download time: %s\n\n", fname[len(fname)-1], time.Since(t))
}

func main() {
	var wg sync.WaitGroup
	ll, err := os.Open(url_list)
	if err != nil {
		fmt.Println(err)
	}
	defer ll.Close()
	scanner := bufio.NewScanner(ll)
	for scanner.Scan() {
		wg.Add(1)
		//download(scanner.Text())
		go func(url string) {
			defer wg.Done()
			download(url)
		}(scanner.Text())
	}
	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}
	wg.Wait()
}
dnb ★★ ()
Ответ на: комментарий от dnb

Ну, там рабочий пример с каналами и горутинами. Ты сам смотри, но изучить я бы рекомендовал.

BigAlex ★★★ ()
Последнее исправление: BigAlex (всего исправлений: 1)
Ответ на: комментарий от dnb

Проще разобраться, что он там делал, чем задавать общие вопросы.

BigAlex ★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.