2014-07-07 1 views
13

Go 언어에는 여러 채널을 폴링하고 어떤 채널이 비어 있지 않은지에 따라 특정 작업을 수행하는 데 사용할 수있는 select 문이 있습니다.Haskell STM 채널에 대한 Go의 select 문을 구현하는 방법은 무엇입니까?

예. chanA, chanB 또는 chanC 하나가 비어 때까지

select { 
    case a := <- chanA: 
    foo(a) 
    case b := <- chanB: 
    baz(b) 
    case c := <- chanC: 
    bar(c) 
} 

이는 예를 chanB 비 비어위한 경우, 그것은 chanB에서 읽습니다, 대기 및 b에 결과를 저장 한 후 baz(b)를 호출합니다. default: 절도 추가 할 수 있습니다. 즉, select 문이 채널에서 대기하지 않고 대신 모든 채널이 비어있는 경우 default 절이 무엇이든합니다.

하스켈에서 STM TChan을 구현하는 가장 좋은 방법은 무엇입니까? 그것은 if-else chain에 의해 순진하게 행해질 수 있습니다 : 각 채널이 isEmptyChan인지 확인하고, 비어 있지 않다면 읽은 다음 적절한 함수를 호출하십시오. 그렇지 않으면 모든 채널이 비어있는 경우 retry을 호출하십시오. 더 우아한/관용적 인 방법이 있을지 궁금 해서요?

Go의 select 문에는 해당 경우에 send 문도 포함될 수 있으며 채널이 비어있는 경우에만 send 문을 완료한다는 점에 유의하십시오. 비록 그렇게 할 수있는 우아한 방법이 있을지는 모르겠지만 기능이 복제 될 수 있다면 좋을 것입니다.

만 약간 관련이 있지만, 뭔가 난 그냥 눈치 내가 그것을 게시 할 경우 확실하지 않다 :

"구현 한 때까지 스레드를 차단할 수 : retry에 대한 설명에서 Control.Monad.STM 페이지에 오타가있다 읽은 TVars 중 입니다. "

+1

'r 에이스'Control.Concurrent.Async'. –

+4

go는 사용 가능한 첫 번째 작업을 수행하지 않지만 사용할 수있는 임의의 작업을 무작위로 수행한다는 점에 유의할 가치가 있습니다. 나중에 정의되었거나 선택 경로에서 불운하기 때문에 특별히 채널을 굶기는 않습니다. – Dustin

+0

이것은 Go의'select'와는 완전히 다릅니다. Go의 채널은'TChan'과 달리 (실제로 유용하게 만들어집니다.)'select'는 보내기 작업과 함께 사용할 수 있습니다. – rightfold

답변

5

는 기아

foreverK :: (a -> m a) -> a -> m() 
foreverK loop = go 
where go = loop >=> go 

-- Existential, not really required, but feels more like the Go version 
data ChanAct = Action (TChan a) (a -> STM()) 

perform :: STM() 
perform (Action c a) = readTChan c >>= a 

foreverSelectE :: [ChanAct] -> STM() 
foreverSelectE = foreverSelect . map perform 

foreverSelect :: [STM()] -> STM() 
foreverSelect = foreverK $ \xs -> first xs >> return (rotate1 xs) 

-- Should only be defined for non-empty sequences, but return() is an okay default. 
-- Will NOT block the thread, but might do nothing. 
first :: [STM()] -> STM() 
first = foldr orElse (return()) 

-- Should only be defined for non-empty sequences, really. 
-- Also, using a list with O(1) viewL and snoc could be better. 
rotate1 :: [a] -> [a] 
rotate1 [] = [] 
rotate1 (h:t) = t ++ [h] 

example = foreverSelectE 
    [ Action chanA foo 
    , Action charB baz 
    , Action chanC bar 
    ] 

영원히 피하려면 피할 수, 당신은 대신 mkSelect :: [STM()] -> STM (STM())이 "가죽"가 할 수있는 TVAR [STM()] 각을 회전 그것은처럼 사용됩니다

example1 :: STM() 
example1 = do 
    select <- mkSelect [actions] -- Just set-up 
    stuff1 
    select -- does one of the actions 
    stuff2 
    select -- does one of the actions 

main = OpenGL.idleCallback $= atomically example1 

이 기술을 확장하면 모든 동작이 차단 될 때까지 동작을 수행했는지 또는 어떤 동작을 수행했는지 또는 반복했는지 등을보고 할 수 있습니다.

11

당신은 orElse 사용 (읽기 및 쓰기 모두) select 의미 구현할 수 있습니다 (참고 :이 GHC에 따라 다릅니다.) 예를 들어 :

forever $ atomically $ 
    writeTChan chan1 "hello" `orElse` writeTChan chan2 "world" `orElse` ... 

아이디어는 그 때 하나의 동작 재시도 (예를 들어, 당신은 챈에게 글을 쓰고 있지만, 꽉 찬 것입니다, 또는 첸에서 읽으신 것이지만 그것은 비어 있습니다), 두 번째 액션이 수행됩니다. default 문은 체인의 마지막 동작으로 return()입니다.

추가 : @Dustin이 지적했듯이, 좋은 이유 때문에 임의의 분기를 선택합니다. 아마 가장 쉬운 해결책은 각 반복마다 액션을 섞는 것입니다. 대부분의 경우 괜찮습니다. 이동 의미론을 올바르게 복제하면 (활성 분기 만 셔플) 조금 더 어렵습니다. 아마도 모든 브랜치에 대해 수동으로 isEmptyChan을 검사하는 것이 좋습니다.

+3

더스틴 (Dustin)이 위에 언급 한 것처럼 기아에 문제가 있지 않습니까? – Dan

+3

@Dan 나는 당신이 옳다고 생각한다. (Yuras의 예제가'readTChan'을 사용한다고 가정하면)'readTChan chanN'은 모든 chans <'N'이 비어있는 동안에 만 읽을 수 있습니다. 따라서 굶주림의 가능성은 처음에 예상했던 것보다 훨씬 더 심각합니다. – jberryman

+0

@ 댄 당신 말이 맞아요, 나는 메모를 추가했습니다. – Yuras

관련 문제