구독해 주셔서 감사합니다

소개

처음 고의 채널 작업을 시작했을 때 채널을 데이터 구조로 생각하는 실수를 저질렀습니다. 나는 고 루틴 사이에 자동으로 동기화 된 액세스를 제공하는 큐로 채널을 보았다. 이 구조적 이해로 인해 많은 나쁘고 복잡한 동시 코드를 작성하게되었습니다.

나는 시간이 지남에 따라 채널이 어떻게 구성되어 있는지 잊고 그들이 어떻게 행동하는지에 집중하는 것이 가장 좋다는 것을 배웠다. 이 채널에 올 때 그래서 지금,나는 한 가지에 대해 생각:신호. 채널은 하나의 고 루틴이 특정 이벤트에 대해 다른 고 루틴을 신호 할 수있게합니다. 신호는 채널로 수행해야하는 모든 것의 핵심입니다. 채널을 신호 메커니즘으로 생각하면 잘 정의되고 더 정확한 동작으로 더 나은 코드를 작성할 수 있습니다.

신호가 어떻게 작동하는지 이해하려면 세 가지 속성을 이해해야합니다:

  • 배달 보증
  • 상태
  • 데이터 유무에 관계없이

이 세 가지 속성은 함께 작동하여 시그널링에 대한 디자인 철학을 만듭니다. 이러한 속성에 대해 논의한 후 이러한 속성이 적용된 시그널링을 보여주는 여러 코드 예제를 제공합니다.

배달 보증

배달 보증은”특정 고루틴이 보낸 신호가 수신되었다는 보장이 필요합니까?”

즉,목록 1 에서이 예를 감안할 때:

목록 1

01 go func() {02 p := <-ch // Receive03 }()0405 ch <- "paper" // Send

송신 고루틴은 05 호선의 채널을 통해 송신 된paper가 이동하기 전에 02 호선의 고 루틴에 의해 수신되었다는 보증이 필요합니까?

이 질문에 대한 답을 바탕으로 버퍼링되지 않은 채널 또는 버퍼링된 채널의 두 가지 유형 중 어떤 채널을 사용할지 알 수 있습니다. 각 채널은 배달 보장과 관련하여 다른 동작을 제공합니다.

그림 1:배달 보증

배달 보증

보장은 중요하며,그렇게 생각하지 않으면 나는 당신을 팔고 싶은 물건을 많이 가지고 있습니다. 당연히,나는 농담을 한것을 해보고 있다,그러나 너는 생활안에 개런티가 있지 않을 때 너는 긴장하지 않는가? 동시 소프트웨어를 작성할 때 보증이 필요한지 여부에 대한 강한 이해를 갖는 것이 중요합니다. 우리가 계속,당신은 결정하는 방법을 배울 수 있습니다.

상태

채널의 동작은 현재 상태에 의해 직접적으로 영향을 받는다. 채널의 상태는 전무,개방 또는 폐쇄 될 수 있습니다.

아래 목록 2 는 이러한 세 가지 상태 각각에 채널을 선언하거나 배치하는 방법을 보여줍니다.

목록 2

// ** nil channel// A channel is in a nil state when it is declared to its zero valuevar ch chan string// A channel can be placed in a nil state by explicitly setting it to nil.ch = nil// ** open channel// A channel is in a open state when it's made using the built-in function make.ch := make(chan string) // ** closed channel// A channel is in a closed state when it's closed using the built-in function close.close(ch)

상태는 송신 및 수신 작업의 동작 방식을 결정합니다.

신호는 채널을 통해 송수신된다.

그림 2:상태

상태

채널이 전무 상태에 있으면 채널에서 시도된 송신 또는 수신이 차단됩니다. 채널이 열린 상태에있을 때,신호를 송수신 할 수있다. 채널이 닫힌 상태로 배치되면 신호는 더 이상 보낼 수 없지만 여전히 신호를 수신 할 수 있습니다.

이러한 상태는 발생하는 상황에 따라 필요한 다른 동작을 제공합니다. 납품의 보증과 국가를 결합 할 때,당신은 당신이 당신의 디자인 선택의 결과로 초래되는 비용/혜택을 분석을 시작할 수 있습니다. 또한 대부분의 경우 코드를 읽는 것만으로 버그를 빠르게 발견할 수 있습니다.

데이터 유무에 관계없이

마지막으로 고려해야 할 시그널링 특성은 데이터 유무에 관계없이 신호를 보내야 하는지 여부입니다.

채널에서 송신을 수행하여 데이터로 신호를 보냅니다.

목록 3

01 ch <- "paper"

당신이 데이터로 신호를 보낼 때,그것은 보통 때문입니다:

  • 고루틴은 새로운 작업을 시작하라는 요청을 받고 있습니다.
  • 고루틴은 결과를 보고합니다.

채널을 닫아 데이터 없이 신호를 보냅니다.

목록 4

01 close(ch)

당신이 자료 없이 신호할 때,그것은 보통 때문에 입니다:

  • 고루틴은 그들이 하는 일을 멈추라는 말을 듣고 있다.
  • 고루틴은 결과 없이 완료되었다고 보고합니다.
  • 고루틴은 처리가 완료되고 종료되었다고보고합니다.

이 규칙에는 예외가 있지만 주요 사용 사례이며 이 게시물에서 초점을 맞출 것입니다. 이 규칙에 대한 예외는 초기 코드 냄새라고 생각합니다.

데이터 없이 신호를 보내는 것의 한 가지 이점은 단일 고루틴이 한 번에 많은 고루틴을 신호할 수 있다는 것이다. 데이터 시그널링은 항상 고 루틴 간의 1 대 1 교환입니다.

데이터로 시그널링

데이터로 시그널링하려면 필요한 보증 유형에 따라 세 가지 채널 구성 옵션을 선택할 수 있습니다.

그림 3 : 데이터 신호

3 개의 채널 옵션은 버퍼링되지 않음,버퍼링된>1 또는 버퍼링된=1 입니다.

  • 보증

    • 버퍼링되지 않은 채널은 전송 중인 신호가 수신되었음을 보증합니다.
      • 신호의 수신은 신호의 송신이 완료되기 전에 발생하기 때문이다.
  • 보증 없음

    • 크기>1 의 버퍼링된 채널은 전송 중인 신호가 수신되었다는 보증을 제공하지 않습니다.
      • 신호의 송신은 신호의 수신이 완료되기 전에 발생하기 때문이다.
  • 지연 보증

    • 크기=1 의 버퍼링된 채널은 지연 보증을 제공합니다. 그것은 전송 된 이전 신호가 수신 된 것을 보장 할 수 있습니다.
      • 첫 번째 신호의 수신 때문에,제 2 신호의 송신이 완료되기 전에 일어난다.

버퍼의 크기는 난수가되어서는 안되며 잘 정의 된 제약 조건에 대해 항상 계산되어야합니다. 컴퓨팅에는 무한대가 없으며 모든 것은 시간이든 공간이든 잘 정의 된 제약 조건이 있어야합니다.

데이터 없는 시그널링

데이터 없는 시그널링은 주로 취소를 위해 예약되어 있다. 한 고루틴이 다른 고루틴에게 신호를 보내서 그들이 하는 일을 취소하고 계속 나아갈 수 있게 합니다. 버퍼링되지 않은 채널과 버퍼링 된 채널을 모두 사용하여 취소를 구현할 수 있지만 데이터가 전송되지 않을 때 버퍼링 된 채널을 사용하는 것은 코드 냄새입니다.

그림 4 : 데이터 없는 신호

내장 함수close은 데이터 없이 신호를 보내는 데 사용됩니다. 상태 섹션에서 위에서 설명한 바와 같이,당신은 여전히 닫힌 채널에서 신호를 수신 할 수 있습니다. 실제로 닫힌 채널에서의 수신은 차단되지 않으며 수신 작업은 항상 반환됩니다.

대부분의 경우 표준 라이브러리context패키지를 사용하여 데이터 없이 시그널링을 구현하려고 합니다. context패키지는 시그널링을 위해 아래에 버퍼링되지 않은 채널을 사용하고,내장 함수close은 데이터 없이 신호를 보낸다.

컨텍스트 패키지가 아닌 자신의 채널을 취소에 사용하도록 선택한 경우 채널 유형이chan struct{}이어야 합니다. 이 신호에만 사용되는 채널을 표시하는 제로 공간,관용적 방법입니다.

시나리오

이러한 특성을 그대로 사용하면 실제로 작동하는 방식을 더 잘 이해하는 가장 좋은 방법은 일련의 코드 시나리오를 실행하는 것입니다. 나는 채널 기반 코드를 읽고 쓸 때 고 루틴을 사람들로 생각하는 것을 좋아합니다. 이 시각화는 정말 도움이,나는 아래의 도움으로 사용합니다.

데이터 보증 버퍼링되지 않은 채널이 있는 신호

전송 중인 신호가 수신되었음을 알아야 할 경우 두 가지 시나리오가 적용됩니다. 이들은 작업 대기 및 결과 대기입니다.

시나리오 1-작업 대기

관리자가되어 새로운 직원을 고용하는 것에 대해 생각해보십시오. 이 시나리오에서는 새 직원이 작업을 수행하기를 원하지만 준비가 될 때까지 기다려야합니다. 이 때문에 그들은 시작 하기 전에 그들을 종이 손을 해야 합니다.

목록 5
https://play.golang.org/p/BnVEHRCcdh

01 func waitForTask() {02 ch := make(chan string)0304 go func() {05 p := <-ch0607 // Employee performs work here.0809 // Employee is done and free to go.10 }()1112 time.Sleep(time.Duration(rand.Intn(500)) * time.Millisecond)1314 ch <- "paper"15 }

리스트 5 의 라인 02 에서,버퍼링되지 않은 채널은string데이터가 신호와 함께 전송될 것이라는 속성으로 생성된다. 그런 다음 라인 04 에서 직원을 고용하고 작업하기 전에 라인 05 에서 신호를 기다리라고 말합니다. 줄 05 는 당신이 보낼 종이 조각을 기다리는 동안 직원이 차단하는 원인이 수신 채널입니다. 종이가 직원에 의해 수신되면,직원은 작업을 수행 한 후 수행하고 갈 무료입니다.

관리자로서 당신은 당신의 새로운 직원과 동시에 작업하고 있습니다. 그래서 당신은 라인 04 에 직원을 고용 한 후,당신은 자신을 발견(라인 12)차단 해제 및 직원 신호를 할 필요가 일을. 참고,그것은 당신이 보낼 필요가 종이 조각을 준비하는 데 걸리는 얼마나 알 수 있었다.

결국 당신은 직원을 신호 할 준비가 된 것입니다. 14 행에서는 데이터를 사용하여 신호를 수행합니다.데이터는 종이 조각입니다. 버퍼링되지 않은 채널이 사용되기 때문에 보내기 작업이 완료되면 직원이 용지를 받았다는 보증을 받을 수 있습니다. 수신은 보내기 전에 발생합니다.

기술적으로 알고 있는 것은 채널 보내기 작업이 완료될 때까지 직원이 종이를 가지고 있다는 것입니다. 두 채널 작업 후 스케줄러는 원하는 문을 실행하도록 선택할 수 있습니다. 사용자 또는 직원이 실행하는 다음 코드 줄은 비결정적입니다. 이 인쇄 문을 사용하는 것은 사물의 순서에 대해 당신을 속일 수 있다는 것을 의미합니다.

시나리오 2-결과를 기다립니다

이 다음 시나리오에서는 상황이 반전됩니다. 이 시간 당신은 당신의 새로운 직원이 고용 될 때 즉시 작업을 수행 할,당신은 자신의 작업의 결과를 기다릴 필요가있다. 당신은 당신이 계속하기 전에 당신이 그들로부터 종이를 필요로하기 때문에 기다릴 필요가있다.

목록 6
https://play.golang.org/p/VFAWHxIQTP

01 func waitForResult() {02 ch := make(chan string)0304 go func() {05 time.Sleep(time.Duration(rand.Intn(500)) * time.Millisecond)0607 ch <- "paper"0809 // Employee is done and free to go.10 }()1112 p := <-ch13 }

리스트 6 의 라인 02 에서,버퍼링되지 않은 채널은string데이터가 신호와 함께 전송될 것이라는 속성으로 생성된다. 그런 다음 라인 04 에서 직원이 고용되어 즉시 일하게됩니다. 당신이 선 04 에 직원을 고용한 후에,당신은 서류상 보고를 기다리는 선 12 에 다음 찾아낸다.

직원이 라인 05 에서 작업을 완료하면,그들은 데이터와 함께 채널 전송을 수행하여 라인 07 에 결과를 보냅니다. 이 채널은 버퍼링되지 않은 채널이므로 전송 전에 수신이 발생하고 직원이 결과를 수신했음을 보장합니다. 일단 직원이이 보증,그들은 완료 하 고 갈 무료. 이 시나리오에서는 직원이 작업을 완료하는 데 얼마나 오래 걸릴지 알 수 없습니다.

비용/편익

버퍼링되지 않은 채널은 전송되는 신호가 수신되었음을 보장합니다. 이 중대하다,하지만 아무것도 무료입니다. 이 보증의 비용은 알 수없는 대기 시간입니다. 작업 시나리오에 대 한 대기에서 직원 아무 생각이 얼마나 오래 그 종이를 보낼 당신을 위해 걸릴 것 이다. 결과 대본안에,너에게 저 결과를 보내기 위하여 직원이 걸리기 위하여 가고 있는 까 얼마나 너는 아이디어가 있지 않는다.

두 시나리오 모두에서 이 알 수 없는 대기 시간은 보증이 필요하기 때문에 함께 사용해야 합니다. 이 보장 된 동작 없이는 논리가 작동하지 않습니다.

데이터 신호-보증 없음-버퍼링 채널>1

당신이 전송되는 신호가 수신 된 것을 알 필요가 없을 때,이 두 가지 시나리오는 플레이에 와서:팬 아웃 및 드롭.

버퍼링된 채널에는 전송 중인 데이터를 저장하는 데 사용할 수 있는 잘 정의된 공간이 있습니다. 얼마나 많은 공간이 필요한지 어떻게 결정합니까? 이 질문에 답하십시오:

  • 내가 완료해야 할 작업의 잘 정의 된 금액이 있습니까?
    • 얼마나 많은 일이 있습니까?
  • 내 직원이 계속 할 수없는 경우,나는 어떤 새로운 작업을 폐기 할 수 있습니까?
    • 얼마나 뛰어난 작품 능력에 나를두고?
  • 프로그램이 예기치 않게 종료되는 경우 어떤 위험 수준을 기꺼이 수락할 수 있습니까?
    • 버퍼에서 대기 중인 모든 항목이 손실됩니다.

이러한 질문이 모델링하는 동작에 대해 이해가되지 않으면 1 보다 큰 버퍼링 된 채널을 사용하는 것이 잘못된 코드 냄새 일 수 있습니다.

시나리오 1-팬 아웃

팬 아웃 패턴을 사용하면 잘 정의 된 수의 직원을 동시에 작업하는 문제에 던질 수 있습니다. 모든 작업에 대해 한 명의 직원이 있기 때문에 얼마나 많은 보고서를 받게 될지 정확히 알 수 있습니다. 너는 모든 그 보고를 받는 너의 상자안에 공간 우측 총계 있는다 것 을 확인할 수 있는다. 이것은 그들의 보고를 복종시키기 위하여 너를 기다린것을 필요로 하지 않는 너의 직원의 이득이 있는다. 그러나 그들은 또는 같은 시간에 가까운 상자에 도착 하는 경우 귀하의 상자에 보고서를 배치 하는 각 차례 걸릴 필요가.

당신이 다시 관리자이지만 이번에는 직원 팀을 고용한다고 상상해보십시오. 너는 너가 각 직원을 실행하는 원하는 개인적인 업무가 있는다. 각 개인적인 직원이 그들의 업무를 끝내는 때,그들은 너의 책상에 너의 상자안에 두어야 하는 서류상 보고에 너를 제공한것을 필요로 한다.

목록 7
https://play.golang.org/p/8HIt2sabs_

01 func fanOut() {02 emps := 2003 ch := make(chan string, emps)0405 for e := 0; e < emps; e++ {06 go func() {07 time.Sleep(time.Duration(rand.Intn(200)) * time.Millisecond)08 ch <- "paper"09 }()10 }1112 for emps > 0 {13 p := <-ch14 fmt.Println(p)15 emps--16 }17 }

목록 7 의 라인 03 에서 버퍼링 된 채널은string데이터가 신호와 함께 전송된다는 속성으로 생성됩니다. 이번에는 라인 02 에 선언 된emps변수 덕분에 20 개의 버퍼로 채널이 생성됩니다.

05 호선~10 호선 사이에 20 명의 직원이 고용되어 즉시 직장에 도착합니다. 각 직원이 라인 07 에 걸릴 것입니다 얼마나 당신은 아무 생각이 없습니다. 그런 다음 08 행에서 직원은 종이 보고서를 보내지만 이번에는 송신이 수신 대기 중임을 차단하지 않습니다. 각 직원에 대한 상자에 공간이 있기 때문에 채널에서 보내기는 같은 시간에 또는 거의 보고서를 보내려는 다른 직원과만 경쟁합니다.

12~16 행 사이의 코드는 모두 당신입니다. 이것은 그들의 일을 끝내고 그들의 보고를 보내기 위하여 너가 모든 20 명의 직원을 기다리는 곳에 이다. 12 행에서는 루프에 있고 13 행에서는 보고서를 기다리는 채널에서 차단됩니다. 일단 보고서가 수신되면,보고서는 라인 14 에 인쇄되고 로컬 카운터 변수는 직원이 완료되었음을 나타내기 위해 감소된다.

시나리오 2-드롭

드롭 패턴을 사용하면 직원이 용량에있을 때 작업을 버릴 수 있습니다. 이것에는 너의 클라이언트에게서 일을 받아들이기에 계속하기의 이득이 결코 저 일의 합격에 있는 배압 또는 잠복 적용 있지 않는다. 너가 수용량에 진실하게 이을 때 여기의 열쇠는 있있다 그래서 너는 일의 총계에 밑에 또는 넘어서 투입하지 않는다 너는 끝낸것을 시도할 것이다. 일반적으로 통합 테스트 또는 메트릭은 이 번호를 식별하는 데 필요한 것입니다.

당신이 다시 관리자이고 일을 끝내기 위해 한 명의 직원을 고용한다고 상상해보십시오. 너는 너가 직원을 실행하는 원하는 개인적인 업무가 있는다. 직원이 그들의 업무를 끝내는 때 너는 그들이 행해졌다는 것을 알 위하여 걱정하지 않는다. 중요한 것은 당신이 상자에 새로운 일을 할 수 있는지 또는 할 수 없는지 여부입니다. 당신이 보내기를 실행할 수 없는 경우에,당신은 당신의 상자가 가득 차있고 직원이 수용량에 다는 것을 알고 있다. 이 시점에서 새로운 작업은 일이 계속 움직일 수 있도록 폐기되어야합니다.

목록 8
https://play.golang.org/p/PhFUN5itiv

01 func selectDrop() {02 const cap = 503 ch := make(chan string, cap)0405 go func() {06 for p := range ch {07 fmt.Println("employee : received :", p)08 }09 }()1011 const work = 2012 for w := 0; w < work; w++ {13 select {14 case ch <- "paper":15 fmt.Println("manager : send ack")16 default:17 fmt.Println("manager : drop")18 }19 }2021 close(ch)22 }

리스팅 8 의 라인 03 에서,버퍼링된 채널은string데이터가 신호와 함께 전송될 속성으로 생성된다. 이번에는 라인 02 에 선언 된cap상수 덕분에 5 개의 버퍼로 채널이 생성됩니다.

05~09 행 사이에 작업을 처리하기 위해 한 명의 직원이 고용됩니다. for range이 채널 수신에 사용됩니다. 종이 조각을 받을 때마다 그것은 라인 07 에 처리 됩니다.

11~19 줄 사이에 직원에게 20 장의 종이를 보내려고 시도합니다. 이번에는select문을 사용하여 줄 14 에서 첫 번째case내부 송신을 수행합니다. default절이 라인 16 의select내부에서 사용되고 있기 때문에 버퍼에 더 이상 공간이 없기 때문에 송신이 차단 될 경우 송신은 라인 17 을 실행하여 포기됩니다.

마지막으로 라인 21 에서 내장 함수close이 채널에 대해 호출됩니다. 이것은 그들의 할당 된 작업을 완료하면 그들이 수행하고 갈 무료 직원에게 데이터없이 신호합니다..

비용/편익

1 보다 큰 버퍼링된 채널은 전송되는 신호가 수신된다는 보장을 제공하지 않습니다. 이 보증에서 벗어나면 두 고 루틴 간의 통신이 지연되거나 지연되지 않는 이점이 있습니다. 팬 아웃 시나리오에서는 보고서를 보낼 각 직원에 대한 버퍼 공간이 있습니다. 드롭 시나리오에서 버퍼는 용량에 대해 측정되며 용량에 도달하면 작업이 삭제되어 계속 이동할 수 있습니다.

두 옵션 모두에서 이러한 보증의 부족은 대기 시간 감소가 더 중요하기 때문에 우리가 살아야 할 것입니다. 0 에서 최소 대기 시간의 요구 사항은 시스템의 전체 논리에 문제가 되지 않습니다.

데이터가 포함된 신호-지연 보증-버퍼링 채널 1

새 신호를 보내기 전에 전송된 이전 신호가 수신되었는지 알아야 할 경우 작업 대기 시나리오가 실행됩니다.

시나리오 1-작업 대기

이 시나리오에서는 새 직원이 있지만 둘 이상의 작업을 수행합니다. 당신은 그들에게 많은 작업,다른 후 하나를 공급하는 것입니다. 그들은 새로운 하나를 시작하기 전에 그러나,그들은 각각의 개별 작업을 완료해야합니다. 한 번에 하나의 작업에서만 작업 할 수 있기 때문에 작업 핸드 오프 사이에 대기 시간 문제가있을 수 있습니다. 대기 시간이 직원이 다음 작업을 수행한다는 보증을 잃지 않고 줄일 수 있다면 도움이 될 수 있습니다.

이것은 1 의 버퍼링 된 채널이 이점을 갖는 곳입니다. 모두가 너와 직원사이 예기한 걸음에 달리면,너의 어느 쪽도 아니는 다른 사람을 기다린것을 필요로 하지 않을 것이다. 종이를 보낼 때마다 버퍼가 비어 있습니다. 직원이 더 많은 작업에 도달 할 때마다 버퍼가 가득 찼습니다. 그것은 작업 흐름의 완벽한 대칭입니다.

가장 좋은 부분은 이것입니다. 언제 든 지 종이 조각을 보내려고 시도 하 고 버퍼가 가득 차 있기 때문에 수 없습니다,만약 당신이 알고 당신의 직원 문제가 있고 중지. 이것은 지연된 보장이 들어오는 곳입니다. 버퍼가 비어 있고 전송을 수행하면 직원이 마지막으로 보낸 작업을 수행했음을 보증합니다. 당신이 보내기를 수행하고 당신이 할 수없는 경우,당신은 그들이하지 않은 보장이 있습니다.

목록 9
https://play.golang.org/p/4pcuKCcAK3

01 func waitForTasks() {02 ch := make(chan string, 1)0304 go func() {05 for p := range ch {06 fmt.Println("employee : working :", p)07 }08 }()0910 const work = 1011 for w := 0; w < work; w++ {12 ch <- "paper"13 }1415 close(ch)16 }

목록 9 의 라인 02 에서 크기 1 의 버퍼링 된 채널은string데이터가 신호와 함께 전송된다는 속성으로 생성됩니다. 라인 사이에 04 08 을 통해 하나의 직원이 작업을 처리하기 위해 고용된다. for range이 채널 수신에 사용됩니다. 종이 조각을 받을 때마다 그것은 라인 06 에 처리 됩니다.

10~13 행 사이에 작업을 직원에게 보내기 시작합니다. 당신이 보낼 수 있는 것처럼 당신의 직원이 빨리 달릴 수 있는 경우에,당신 2 사이 대기 시간은 감소된다. 하지만 각 전송 성공적으로 수행,당신은 당신이 제출한 작품의 마지막 조각에 근무 되 고 보장.

마지막으로 라인 15 에서 내장 함수close이 채널에 대해 호출됩니다. 이것은 그들이 행해지고 가게 자유롭게 인 직원에게 자료 없이 신호할 것이다. 그러나 제출한 마지막 작품은for range이 종료되기 전에 접수(플러시)됩니다.

데이터 없는 신호-컨텍스트

이 마지막 시나리오에서는context패키지의Context값을 사용하여 실행 중인 고루틴을 취소하는 방법을 확인할 수 있습니다. 이 모든 것은 데이터없이 신호를 수행하기 위해 닫힌 버퍼링되지 않은 채널을 활용하여 작동합니다.

당신은 마지막으로 관리자이고 당신은 작업을 수행하기 위해 하나의 직원을 고용. 이 시간 직원 완료에 대 한 시간의 일부 알 수 없는 금액에 대 한 대기 하고자 하지 않습니다. 개별 마감 시간에 고 직원 시간에 완료 하지 않는 경우 기다릴 수 없습니다.

목록 10
https://play.golang.org/p/6GQbN5Z7vC

01 func withTimeout() {02 duration := 50 * time.Millisecond0304 ctx, cancel := context.WithTimeout(context.Background(), duration)05 defer cancel()0607 ch := make(chan string, 1)0809 go func() {10 time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)11 ch <- "paper"12 }()1314 select {15 case p := <-ch:16 fmt.Println("work complete", p)1718 case <-ctx.Done():19 fmt.Println("moving on")20 }21 }

목록 10 의 라인 02 에서 직원이 작업을 완료해야하는 시간을 나타내는 기간 값이 선언됩니다. 이 값은 줄 04 에서 시간 초과가 50 밀리초인context.Context값을 만드는 데 사용됩니다. context패키지의WithTimeout함수는Context값과 취소 함수를 반환합니다.

context패키지는 지속 시간이 충족되면Context값과 연결된 버퍼링되지 않은 채널을 닫는 고루틴을 만듭니다. 당신은 호출에 대한 책임이 있습니다cancel에 관계없이 일이 판명 방법의 함수. 이것은Context를 위해 만들어진 것들을 정리할 것입니다. cancel함수를 두 번 이상 호출해도 괜찮습니다.

라인 05 에서cancel함수는 이 함수가 종료되면 실행되도록 지연됩니다. 라인 07 에 버퍼링 된 채널 1 이 생성되어 직원이 작업 결과를 보내는 데 사용됩니다. 그런 다음 라인 09 에서 12 까지 직원을 고용하고 즉시 일하게됩니다. 당신은 직원이 완료 걸릴 것입니다 얼마나 오래 아무 생각이 없습니다.

14~20 행 사이에서select문을 사용하여 두 채널에서 수신합니다. 직원이 당신에게 자신의 결과를 보낼 수 있도록 라인(15)에 수신,당신은 기다립니다. 18 행에서 수신하면context패키지가 50 밀리 초가 올랐다는 신호를 받는지 기다립니다. 먼저 수신 어느 신호는 처리 하나가 될 것입니다.

이 알고리즘의 중요한 측면은 1 의 버퍼링 된 채널을 사용하는 것입니다. 직원이 시간안에 끝내지 않으면,너는 직원에게 어떤 고시를 주기없이 위에 이동하고 있다. 직원의 관점에서,그들은 항상 당신에게 라인(11)에 보고서를 보내드립니다 당신이 거기 또는 그것을받을 수없는 경우 그들은 장님. 당신이 버퍼링되지 않은 채널을 사용하는 경우,직원은 영원히 당신이 이동하는 경우 당신에게 보고서를 보내려고 차단합니다. 이것은 고 루틴 누출을 만들 것입니다. 그래서 버퍼링 된 채널 1 이 이것을 방지하기 위해 사용되고 있습니다.

결론

채널(또는 동시성)을 사용할 때 보증,채널 상태 및 전송에 대한 시그널링의 속성을 알고 이해하는 것이 중요합니다. 그들은 당신이 작성하는 동시 프로그램 및 알고리즘에 필요한 최상의 동작을 구현하는 데 도움이 될 것입니다. 그들은 당신이 버그를 찾아 잠재적으로 나쁜 코드를 냄새 도움이 될 것입니다.

이 게시물에서는 시그널링의 속성이 다른 시나리오에서 어떻게 작동하는지 보여주는 몇 가지 샘플 프로그램을 공유했습니다. 각 규칙에 예외 있는다 그러나 이 본은 시작할 것이다 좋은 기초 이다.

채널을 효과적으로 생각하고 사용하는 시기와 방법에 대한 요약으로 이러한 개요를 검토합니다.:

언어 역학

  • 채널을 사용하여 고루틴을 조율하고 조정합니다.
    • 데이터 공유가 아닌 시그널링 속성에 중점을 둡니다.
    • 데이터 또는 데이터 없이 신호.
    • 공유 상태에 대한 액세스를 동기화하는 데 사용하는 질문.
      • 채널이 이것에 대해 더 간단 할 수 있지만 처음에는 질문 할 수있는 경우가 있습니다.
  • 버퍼링되지 않은 채널:
    • 수신은 전송 전에 발생합니다.
    • 혜택:신호가 수신 된 100%보증.
    • 비용: 신호가 수신 될 때 알 수없는 대기 시간.
  • 버퍼링 된 채널:
    • 송신은 수신 전에 발생합니다.
    • 이점:신호 간의 차단 대기 시간을 줄입니다.
    • 비용:신호가 수신되었을 때 보장되지 않습니다.
      • 더 큰 완충기,더 적은 보증.1 의
      • 완충기는 당신에게 보증의 1 개의 연기한 보내를 줄 수 있습니다.
  • 닫히는 수로:
    • 마지막은 받기 전에 일어납니다(부드럽게 하는 같이).
    • 데이터 없는 신호.
    • 취소 및 마감 신호에 적합합니다.
  • 전무 채널:
    • 송수신 블록.
    • 신호 끄기
    • 속도 제한 또는 단기 정지에 적합합니다.

디자인 철학

  • 채널에서 주어진 송신이 송신 고루틴을 차단할 수 있는 경우:
    • 1 보다 큰 버퍼링된 채널을 사용할 수 없습니다.
      • 1 보다 큰 버퍼에는 이유/측정값이 있어야 합니다.
    • 때 보내는 고 루틴 블록을 어떻게되는지 알고 있어야합니다.
  • 채널에서 주어진 송신이 송신 고루틴이 차단되지 않는 경우:
    • 각 송신에 대한 정확한 버퍼 수를 갖습니다.
      • 팬 아웃 패턴
    • 최대 용량에 대해 측정 된 버퍼가 있습니다.
      • 드롭 패턴
  • 적은 버퍼가 더 많습니다.
    • 버퍼에 대해 생각할 때 성능에 대해 생각하지 마십시오.
    • 버퍼는 시그널링 간의 차단 대기 시간을 줄이는 데 도움이 될 수 있습니다.
      • 차단 대기 시간을 0 으로 줄이는 것이 반드시 더 나은 처리량을 의미하는 것은 아닙니다.
      • 하나의 버퍼가 당신에게 충분한 처리량을 제공한다면 그것을 유지하십시오.
      • 1 보다 크고 크기를 측정하는 질문 버퍼.
      • 충분한 처리량을 제공하는 가능한 가장 작은 버퍼를 찾습니다.

답글 남기기

이메일 주소는 공개되지 않습니다.