동시 하스켈

Concurrent Haskell

Concurrent Haskell은 명백한 동시성으로 Haskell 98을 확장한다[1].그것의 두 가지 주요 기본 개념은 다음과 같다.

  • 원시형MVar α비어 있거나 유형의 값을 가진 경계/단일 비동기 채널 구현α.
  • 를 통해 동시 스레드를 생성하는 기능forkIO원시적인

맨 위에 구축된 것은 결합되지 않은 채널, 세마포어 및 샘플 변수와 같은 유용한 동시성 및 동기화 추상화[2] 모음입니다.

Haskell 스레드는 오버헤드가 매우 낮다. 생성, 컨텍스트 전환 및 스케줄링은 모두 Haskell 런타임의 내부 작업이다.이러한 Haskell 레벨 스레드는 구성 가능한 수의 OS 레벨 스레드에 매핑되며, 대개 프로세서 코어당 하나씩입니다.

소프트웨어 트랜잭션 메모리

GHC대한 소프트웨어 트랜잭션 메모리[3](STM) 확장은 Concurrent Haskell의 원시 형성에 대한 프로세스를 재사용한다.그러나 STM:

  • 피하다MVar에 찬성하는.TVars
  • 를 소개하다retry그리고orElse원시성, 대체 원자 작용함께 구성될 수 있도록 허용한다.

STM 모나드

STM 모나드[4] Haskell에서 소프트웨어 트랜잭션 메모리를 구현한 것이다.GHC로 구현되며, 거래에서 변이 가능한 변수가 수정될 수 있도록 한다.

전통적인 접근 방식

은행 애플리케이션과 그 안에 있는 거래, 즉 한 계좌에서 돈을 받아 다른 계좌에 넣는 이체 기능을 예로 들어보자.IO 모나드에서는 다음과 같이 보일 수 있다.

타자를 치다 계정 = IORef 정수  전근시키다 :: 정수 -> 계정 -> 계정 -> IO () 전근시키다 분량 로부터  = 하다     fromVal <- readIORef 로부터  - (A)     토벌   <- readIORef      writeIORef 로부터 (fromVal - 분량)     writeIORef  (토벌 + 분량) 

이는 동시동일한 계정으로 여러 개의 전송이 발생할 수 있는 동시 상황에서 문제를 야기한다.계좌이체가 2건인 경우from, 그리고 두 통화 모두 런 라인을 전송한다.(A)두 사람 중 어느 한 사람이 새로운 가치를 쓰기 전에, 이체된 금액 중 하나만 계정에서 삭제된 채, 다른 두 계좌에 돈이 들어갈 가능성이 있다.from그래서 인종적 조건을 만드는 것이다.이렇게 되면 은행 신청이 일관되지 않는 상태가 될 것이다.

그러한 문제에 대한 전통적인 해결책은 자물쇠다.예를 들어, 계정 수정 주위에 자물쇠를 걸어 크레딧과 디비트가 원자적으로 발생하도록 보장할 수 있다.Haskell에서는 MVar를 사용하여 잠금이 수행된다.

타자를 치다 계정 = 엠바르 정수  대변을 보다 :: 정수 -> 계정 -> IO () 대변을 보다 분량 회계 = 하다     현재의 <- 타케엠바르 회계     퍼트엠바르 회계 (현재의 + 분량)  차변 :: 정수 -> 계정 -> IO () 차변 분량 회계 = 하다     현재의 <- 타케엠바르 회계     퍼트엠바르 회계 (현재의 - 분량) 

이러한 절차를 사용하면 개별 계정에 읽기 및 쓰기가 부적절하게 섞여 있어 돈이 손실되거나 이득이 되지 않도록 보장할 수 있다.단, 함께 구성하여 이전과 같은 절차를 만들려고 하는 경우:

전근시키다 :: 정수 -> 계정 -> 계정 -> IO () 전근시키다 분량 로부터  = 하다     차변 분량 로부터     대변을 보다 분량  

경주 조건이 여전히 존재한다. 첫 번째 계정은 차압될 수 있고, 그 다음 실의 실행이 중단되어 계정 전체가 일관되지 않는 상태가 될 수 있다.따라서 복합 연산의 정확성을 보장하기 위해 추가 잠금 장치를 추가해야 하며, 최악의 경우 주어진 연산에 사용되는 개수에 관계없이 모든 계정을 잠글 필요가 있을 수 있다.

원자성거래

이를 피하기 위해 원자 거래를 작성할 수 있는 STM 모나드를 사용할 수 있다.이는 거래 내부의 모든 작업이 완전히 완료됨을 의미하며, 다른 스레드가 우리 거래에서 사용하고 있는 변수를 수정하거나, 실패하거나, 거래가 시작되기 전의 상태로 롤백된다.요컨대 원자력의 거래는 완전히 완료되거나 아예 실행되지 않은 것과 같다.위의 잠금 기반 코드는 비교적 간단한 방법으로 해석된다.

타자를 치다 계정 = 티바르 정수  대변을 보다 :: 정수 -> 계정 -> STM () 대변을 보다 분량 회계 = 하다     현재의 <- readTVar 회계     writeTVar 회계 (현재의 + 분량)  차변 :: 정수 -> 계정 -> STM () 차변 분량 회계 = 하다     현재의 <- readTVar 회계     writeTVar 회계 (현재의 - 분량)  전근시키다 :: 정수 -> 계정 -> 계정 -> STM () 전근시키다 분량 로부터  = 하다     차변 분량 로부터     대변을 보다 분량  

반환 유형:STM ()거래를 위한 스크립트를 작성하고 있음을 나타내는 것으로 간주될 수 있다.실제로 그러한 거래를 실행할 때가 되면, 함수는atomically :: STM a -> IO a사용된다.위의 구현을 통해 실행 중에 (출발 및 도착) 사용하고 있는 변수에 간섭하는 다른 트랜잭션이 없도록 하여 개발자는 위와 같은 경기 조건이 충족되지 않는지 확인할 수 있다.더 많은 개선을 통해 시스템에서 다른 "사업 논리"가 유지되도록 할 수 있다. 즉, 거래가 충분한 돈이 있을 때까지 계좌에서 돈을 가져가려 해서는 안 된다는 것이다.

전근시키다 :: 정수 -> 계정 -> 계정 -> STM () 전근시키다 분량 로부터  = 하다     fromVal <- readTVar 로부터     만일 (fromVal - 분량) >= 0         그때 하다                차변 분량 로부터                대변을 보다 분량          다른 재시도하다 

여기 더retry트랜잭션을 롤백한 다음 다시 시도하는 함수가 사용됨.STM에서 재시도하는 것은 거래 중 참조하는 변수 중 하나가 일부 다른 거래 코드에 의해 수정될 때까지 거래를 다시 실행하려고 시도하지 않는다는 점에서 현명하다.이것은 STM 모나드를 매우 효율적으로 만든다.

전송 기능을 사용하는 예제 프로그램은 다음과 같이 보일 수 있다.

모듈 메인 어디에  수입하다 컨트롤동시 (포크IO) 수입하다 컨트롤동시STM 수입하다 컨트롤모나드 (영원히.) 수입하다 System.Exit (exitSuccessful)  타자를 치다 계정 = 티바르 정수  본래의 = 하다     까딱거리다 <- 뉴 어카운트 10000      <- 뉴 어카운트 4000     repeatIO 2000 $ 포크IO $ 원자력으로 $ 전근시키다 1 까딱거리다      영원히. $ 하다         밥밸런스 <- 원자력으로 $ readTVar 까딱거리다         질밸런스 <- 원자력으로 $ readTVar          putStrLn ("밥의 잔액: " ++ 보여 주다 밥밸런스 ++ ", 질 밸런스: " ++ 보여 주다 질밸런스)         만일 밥밸런스 == 8000             그때 exitSuccessful             다른 putStrLn "또 캐묻는군."  repeatIO :: 정수 -> IO a -> IO a repeatIO 1 m = m repeatIO n m = m >> repeatIO (n - 1) m  뉴 어카운트 :: 정수 -> IO 계정 뉴 어카운트 분량 = 뉴TVARIO 분량  전근시키다 :: 정수 -> 계정 -> 계정 -> STM () 전근시키다 분량 로부터  = 하다     fromVal <- readTVar 로부터     만일 (fromVal - 분량) >= 0         그때 하다                차변 분량 로부터                대변을 보다 분량          다른 재시도하다  대변을 보다 :: 정수 -> 계정 -> STM () 대변을 보다 분량 회계 = 하다     현재의 <- readTVar 회계     writeTVar 회계 (현재의 + 분량)  차변 :: 정수 -> 계정 -> STM () 차변 분량 회계 = 하다     현재의 <- readTVar 회계     writeTVar 회계 (현재의 - 분량) 

"밥 잔고: 8000, 질 잔고: 6000"을 출력해야 한다.여기 더atomicallyIO 모나드에서 STM 작업을 실행하는 데 기능이 사용됨.

참조

  1. ^ 시몬 페이튼 존스 앤드류 D 고든, 그리고 시그존 피네.Concurrent Haskell.ACM SIGPlan-SIGACT 프로그래밍 언어 원리 심포지엄(PoPL) 1996. (현재 구현과 관련하여 일부 섹션은 구식이다.)
  2. ^ Haskell 계층 라이브러리, 제어.Archive.오늘 동시 보관 2012-08-02
  3. ^ 팀 해리스, 사이먼 말로, 사이먼 페이튼 존스, 모리스 헐리.Composable Memory Transactions.ACM PP'05 병렬 프로그래밍의 원칙과 실천에 관한 심포지엄.2005.
  4. ^ 컨트롤동시STM