2013-08-02 4 views
0

을 위반했는데 왜 그 이유는 모르겠습니다.골 루틴이 프로그램

문제는 다음과 같습니다. 친구와 함께 웹 서버를 만들었습니다. 나는 페이지 로딩에서 goroutines을 사용하는 것이 유익 할 것이라고 생각했다. 그래서 내가 앞서 갔고, 그렇게했다 : loadPage 함수를 goroutine으로 부른다. 그러나 이렇게하면 서버가 오류없이 작동을 멈 춥니 다. 빈 페이지를 인쇄합니다. 문제는 함수 자체에 있어야합니다. 무엇인가 goroutine과 충돌합니다.

는 관련 기능은 다음과 같습니다 이동의 일반적인 웹 프레임 워크 구현에

func loadPage(w http.ResponseWriter, path string) { 
    s := GetFileContent(path) 
    w.Header().Add("Content-Type", getHeader(path)) 
    w.Header().Add("Content-Length", GetContentLength(path)) 
    fmt.Fprint(w, s) 
} 
func GetFileContent(path string) string { 
    cont, err := ioutil.ReadFile(path) 
    e(err) 
    aob := len(cont) 
    s := string(cont[:aob]) 
    return s 
} 


func GetFileContent(path string) string { 
    cont, err := ioutil.ReadFile(path) 
    e(err) 
    aob := len(cont) 
    s := string(cont[:aob]) 
    return s 
} 

func getHeader(path string) string { 
    images := []string{".jpg", ".jpeg", ".gif", ".png"} 
    readable := []string{".htm", ".html", ".php", ".asp", ".js", ".css"} 
    if ArrayContainsSuffix(images, path) { 
     return "image/jpeg" 
    } 
    if ArrayContainsSuffix(readable, path) { 
     return "text/html" 
    } 
    return "file/downloadable" 
} 


func ArrayContainsSuffix(arr []string, c string) bool { 
    length := len(arr) 
    for i := 0; i < length; i++ { 
     s := arr[i] 
     if strings.HasSuffix(c, s) { 
     return true 
     } 
    } 
return false 
} 
+0

이 기능에는 이상이 없습니다. goroutines는 데몬 쓰레드와 같습니다. 메인 goroutine이 종료되면 현재의 모든 goroutines는 끝내지 않고 종료됩니다. 이 과정이 끝나기 전에 메인 골 루틴이 종료되지 않았는지 확인하십시오. – LinearZoetrope

+0

네, 확인하는 것이 좋습니다. 또한, 동시에 실행하지 않을 때 어떤 일이 발생하는지 보았습니까? 모든 것이 제대로 작동합니까? 문제는 실제로 goroutines에서 실행중인 문제가 아닐 수도 있습니다. – joshlf

답변

2

이렇게되는 이유는 "loadPage"를 호출하는 HandlerFunc이 요청과 동 기적으로 호출되기 때문입니다. 이동 루틴에서 핸들러를 호출하면 Handler가 실제로 즉시 리턴하여 응답을 즉시 보내 게됩니다. 그래서 빈 페이지를 얻는 것입니다.

당신은 server.go (라인 1096)에서 볼 수 있습니다

serverHandler{c.server}.ServeHTTP(w, w.req) 
if c.hijacked() { 
    return 
} 
w.finishRequest() 

ServeHTTP 기능이 핸들러를 호출하고 즉시 반환대로 "finishRequest"를 호출합니다. 따라서 Handler 함수는 요청을 수행하고자하는 한 차단해야합니다.

go 루틴을 사용하면 실제로 페이지를 더 빠르게 만들 수 없습니다. 필립 (Philip)이 제안했듯이 채널과 함께 한 일 루틴을 동기화하면이 경우에도 도움이되지 않을 것입니다.

실제로 문제의 근원은 전체 파일을 보내기 전에 메모리에 버퍼링하는 ioutil.ReadFile입니다.

파일을 스트리밍하려면 os.Open을 사용해야합니다.io.Copy을 사용하여 파일 내용을 브라우저로 스트리밍하면 청크 분할 인코딩이 사용됩니다. 다음과 같이 보일 것이다

:

f, err := os.Open(path) 
if err != nil { 
    http.Error(w, "Not Found", http.StatusNotFound) 
    return 
} 
n, err := io.Copy(w, f) 
if n == 0 && err != nil { 
    http.Error(w, "Error", http.StatusInternalServerError) 
    return 
} 

여러 이동 루틴에서 작업을 할 필요가 어떤 이유로, sync.WaitGroup을 살펴 경우

. 채널도 작동 할 수 있습니다.

파일 만 제공하려는 경우 FileServer 또는 ServeFile과 같이이 용도로 최적화 된 다른 옵션이 있습니다.

+0

좋은 반응입니다. 감사합니다. 좋은 설명. 우리가이 일을 한 이유는 (즉, readfile 등) 우리는 기본적으로이 프로젝트를 시작하여 골란을 배우기 때문에 가능한 최상의 방법으로 수행되지 않기 때문입니다. 고맙습니다! – Arcticcu

+1

@Arcticcu 그런 경우에는 내장 패키지의 소스 코드를 읽는 것이 좋습니다. 또한 문서가 도움이되지 않을 때 "_test.go"로 끝나는 파일을 찾아보십시오. – Luke

0

, 경로 처리기는 Goroutines로 호출됩니다. 나는. 어느 시점에서 웹 프레임 워크는 go loadPage(...)라고 말할 것입니다. 당신이loadPage 내부 에서 이동 루틴을 호출하는 경우

그래서, 당신은 Goroutines의 수준을 가지고있다.

Go 스케줄러는 실제로 게으르고 두 번째 레벨을 강제 실행하지 않으면 실행하지 않습니다. 따라서 동기화 이벤트를 통해이를 시행해야합니다. 예 : 채널 또는 sync 패키지를 사용하여. 예 : goroutine의 영향이 다른 goroutine 관찰되어야한다면

, 는 잠금과 같은 동기화 메커니즘을 사용하거나 대하여 확립 통신 채널 :

func loadPage(w http.ResponseWriter, path string) { 
    s := make(chan string) 
    go GetFileContent(path, s) 
    fmt.Fprint(w, <-s) 
} 

Go documentation이 말한다 주문.

왜 실제로이 작업을 수행하는 것이 좋은가요? 더 큰 프로젝트에서는 어떻게 든 효율적으로 조정해야 할 많은 수의 Goroutines를 처리 할 수 ​​있습니다. 그렇다면 Goroutine을 아무데도 사용하지 않는 이유는 무엇입니까? 재미있는 사실 : fmt.Printf과 같은 I/O 작업도 동기화 이벤트를 트리거합니다.

관련 문제