golang с использованием таймаутов с каналами
Я использую goroutines / каналы, чтобы проверить, доступен ли список URL-адресов. Вот мой код. Это, кажется, всегда возвращать true. Почему дело timeout не выполняется? Цель состоит в том, чтобы вернуть false, даже если один из URL-адресов недоступен
import "fmt"
import "time"
func check(u string) bool {
time.Sleep(4 * time.Second)
return true
}
func IsReachable(urls []string) bool {
ch := make(chan bool, 1)
for _, url := range urls {
go func(u string) {
select {
case ch <- check(u):
case <-time.After(time.Second):
ch<-false
}
}(url)
}
return <-ch
}
func main() {
fmt.Println(IsReachable([]string{"url1"}))
}
3 ответов
check(u)
будет спать в настоящее goroutine, то есть тот, который работает func
. The select
оператор запускается правильно только после его возвращения, и к этому времени обе ветви запускаются, и среда выполнения может выбрать тот, который ей нравится.
вы можете решить, запустив check
внутри еще одна горутина:
package main
import "fmt"
import "time"
func check(u string, checked chan<- bool) {
time.Sleep(4 * time.Second)
checked <- true
}
func IsReachable(urls []string) bool {
ch := make(chan bool, 1)
for _, url := range urls {
go func(u string) {
checked := make(chan bool)
go check(u, checked)
select {
case ret := <-checked:
ch <- ret
case <-time.After(1 * time.Second):
ch <- false
}
}(url)
}
return <-ch
}
func main() {
fmt.Println(IsReachable([]string{"url1"}))
}
кажется, вы хотите проверить достижимость набора URL-адресов и вернуть true, если один из них доступен. Если timeout долго по сравнению со временем, которое требуется для вращения goroutine, вы можете упростить это, имея только один тайм-аут для всех URL-адресов вместе. Но мы должны убедиться, что канал достаточно большой, чтобы держать ответы от всех проверок, или те, которые не "выиграть" будет блокировать навсегда:
package main
import "fmt"
import "time"
func check(u string, ch chan<- bool) {
time.Sleep(4 * time.Second)
ch <- true
}
func IsReachable(urls []string) bool {
ch := make(chan bool, len(urls))
for _, url := range urls {
go check(url, ch)
}
time.AfterFunc(time.Second, func() { ch <- false })
return <-ch
}
func main() {
fmt.Println(IsReachable([]string{"url1", "url2"}))
}
причина, по которой это всегда возвращает true, заключается в том, что вы вызываете check(u)
в своем select
заявление. Вам нужно вызвать его в рамках процедуры go, а затем использовать select для ожидания результата или тайм-аута.
если вы хотите проверить достижимость нескольких URL-адресов параллельно, вам нужно реструктурировать свой код.
сначала создайте функцию, которая проверяет достижимость одного URL:
func IsReachable(url string) bool {
ch := make(chan bool, 1)
go func() { ch <- check(url) }()
select {
case reachable := <-ch:
return reachable
case <-time.After(time.Second):
// call timed out
return false
}
}
затем вызовите эту функцию из a петля:
urls := []string{"url1", "url2", "url3"}
for _, url := range urls {
go func() { fmt.Println(IsReachable(url)) }()
}
изменить строку
ch := make(chan bool, 1)
to
ch := make(chan bool)
вы открыли асинхронный (=неблокирующий) канал, но вам нужен блокирующий канал, чтобы заставить его работать.