f# - 패턴 - 자바스크립트는 어떻게 작동하는가




F#의 비동기가 실제로 어떻게 작동합니까? (5)

나는 async 와 방법을 배우려고 노력 let! F #에서 일해. 내가 읽은 모든 문서는 혼란스러워 보입니다. Async.RunSynchronously를 사용하여 비동기 블록을 실행하는 것은 무엇입니까? 비동기입니까, 동기화입니까? 모순처럼 보입니다.

설명서에 따르면 Async.StartImmediate는 현재 스레드에서 실행됩니다. 동일한 스레드에서 실행되는 경우 나에게 비동기 적으로 보이지 않습니다. 아니면 비동기가 스레드가 아닌 coroutines와 비슷합니다. 그렇다면 그들이 언제 다시 앞을 내밀 것입니까?

MS 워드 프로세서 인용 :

let 사용하는 코드 줄! 계산을 시작한 다음 스레드는 결과를 사용할 수있을 때까지 일시 중단되고 실행이 계속됩니다.

스레드가 결과를 기다리는 경우 왜 사용해야합니까? 평범한 함수 호출처럼 보입니다.

Async.Parallel은 무엇을합니까? Async <T>의 시퀀스를받습니다. 왜 평범한 함수의 연속을 병렬로 실행하지 않습니까?

나는 여기서 아주 기본적인 것을 놓치고 있다고 생각한다. 내가 알기에 모든 문서와 샘플이 이해되기 시작할 것입니다.


다른 답변들에서 훌륭한 세부 사항들이 많았지 만, 초보자로서 나는 C #과 F #의 차이점에 의해 발목이 잡혔습니다.

F # 비동기 블록은 코드를 실행하는 방법에 대한 레시피 이지 실제로는 아직 실행할 명령이 아닙니다.

당신은 아마도 다른 조리법과 결합하여 귀하의 조리법을 구축 할 수 있습니다 (예 : Async.Parallel). 그런 다음에 만 시스템에 실행을 요청하면 현재 스레드 (예 : Async.StartImmediate) 또는 새 태스크 또는 다양한 다른 방법으로이를 수행 할 수 있습니다.

그래서 당신이하고 싶은 일을 누가해야하는지에 대한 분리입니다.

C # 모델은 종종 'Hot Tasks'라고 불리는데, 그 이유는 F # '콜드 태스크'모델에 비해 정의의 일부로 작업이 시작되기 때문입니다.


몇 가지.

첫째,

let resp = req.GetResponse()

let! resp = req.AsyncGetReponse()

웹 요청이 'at sea'인 수백 밀리 초 동안 (CPU에 대한 영원), 전자는 하나의 스레드 (I / O에서 차단됨)를 사용하는 반면, 후자는 0 스레드를 사용합니다. 이것은 비동기의 가장 일반적인 '승리'입니다. 하드 디스크가 돌아갈 때까지 기다리는 스레드 나 네트워크 요청이 돌아 오기를 기다리지 않는 비 블로킹 I / O를 작성할 수 있습니다. (다른 대부분의 언어와 달리 제어 반전을 강요하지 않고 콜백으로 요소를 넣을 수 있습니다.)

둘째, Async.StartImmediate 는 현재 스레드에서 비동기를 시작 합니다. 일반적인 사용법은 GUI를 사용하는 것입니다. 예를 들어 UI를 업데이트 (예 : "loading ..."라고 말하기)하기를 원하는 GUI 응용 프로그램을 가지고 있고 배경 작업을 수행 한 다음 (디스크에 무엇이든로드 한 다음) 완성 된 UI를 업데이트하기 위해 전경 UI 스레드로 돌아갑니다 ( "완료!"). StartImmediate 사용하면 비동기가 작업 시작시 UI를 업데이트하고 SynchronizationContext 를 캡처하여 작업 마지막에 GUI로 돌아가 UI의 최종 업데이트를 수행 할 수 있습니다.

다음으로, Async.RunSynchronously 은 거의 사용되지 않습니다 (한 논문은 한 번만 앱에서 호출하는 것입니다). 한도 내에서 전체 프로그램을 비동기 적으로 작성한 경우 "main"메서드에서 RunSynchronously 를 호출하여 프로그램을 실행하고 결과를 기다립니다 (예 : 콘솔 응용 프로그램에서 결과를 출력). 이렇게하면 스레드를 차단하므로 일반적으로 프로그램의 비동기 부분 바로 위에 'synch stuff'가있는 경계선에서만 유용합니다. (고급 사용자는 StartWithContinuations 선호 할 것입니다. RunSynchronously 는 비동기에서 다시 동기화하기위한 "쉬운 해킹"입니다.)

마지막으로, Async.Parallel 은 fork-join 병렬 처리를 수행합니다. async (TPL에서와 같은)보다는 함수를 사용하는 유사한 함수를 작성할 수 있지만 F #의 일반적인 스위트 스폿은 이미 비동기 객체 인 병렬 I / O 바인딩 계산이므로 가장 일반적입니다. 유용한 서명. (CPU 경계 병렬 처리의 경우 비동기를 사용할 수 있지만 TPL도 사용할 수 있습니다.)


비동기 블록에서 일부 동기 및 비동기 작업을 수행 할 수 있습니다. 예를 들어 여러 가지 방법으로 사용자의 상태를 표시하는 웹 사이트가있을 수 있으므로 곧 청구될지를 보여줄 수 있습니다. 생일과 숙제가 다가옵니다. 이들 중 어느 것도 동일한 데이터베이스에 없으므로 응용 프로그램에서 세 가지 별도의 호출을 수행합니다. 가장 느린 통화가 끝나면 결과를 함께 표시하여 최종 결과가 가장 느린 것을 기반으로 표시되도록 호출을 병렬로 수행하고자 할 수 있습니다. 이들이 돌아 오는 순서에 관심이 없으면 세 가지가 모두 수신되는 시점을 알고 싶을뿐입니다.

예제를 끝내려면이 정보를 표시 할 UI를 만드는 작업을 동 기적으로 수행해야 할 수 있습니다. 따라서 결국에는이 데이터를 가져오고 UI를 표시하고 순서가 중요하지 않은 부분은 병렬로 처리하고 순서 문제는 동기식으로 수행 할 수 있습니다.

이 작업은 세 개의 스레드로 수행 할 수 있지만 세 번째 작업이 끝나면 원래 스레드를 추적하고 일시 중지 해제해야하지만 더 많은 작업이 필요하므로 .NET Framework에서이 작업을 쉽게 처리 할 수 ​​있습니다.


비동기 사용은 사용중인 스레드 수를 저장하는 것입니다.

다음 예제를 참조하십시오.

let fetchUrlSync url = 
    let req = WebRequest.Create(Uri url)
    use resp = req.GetResponse()
    use stream = resp.GetResponseStream()
    use reader = new StreamReader(stream)
    let contents = reader.ReadToEnd()
    contents 

let sites = ["http://www.bing.com";
             "http://www.google.com";
             "http://www.yahoo.com";
             "http://www.search.com"]

// execute the fetchUrlSync function in parallel 
let pagesSync = sites |> PSeq.map fetchUrlSync  |> PSeq.toList

위의 코드는 당신이 원하는 것입니다 : 함수를 정의하고 병렬로 실행하십시오. 그렇다면 왜 여기에 비동기가 필요합니까?

큰 것을 생각해 봅시다. 예를 들어 사이트 수가 4 개가 아니라 10,000 개라고 가정합니다. 그렇다면 병렬로 실행하는 데는 10,000 개의 스레드가 필요하므로 엄청난 리소스 비용이 소요됩니다.

비동기에서 :

let fetchUrlAsync url =
    async { let req =  WebRequest.Create(Uri url)
            use! resp = req.AsyncGetResponse()
            use stream = resp.GetResponseStream()
            use reader = new StreamReader(stream)
            let contents = reader.ReadToEnd()
            return contents }
let pagesAsync = sites |> Seq.map fetchUrlAsync |> Async.Parallel |> Async.RunSynchronously

코드가 use! resp = req.AsyncGetResponse() 중일 때 use! resp = req.AsyncGetResponse() use! resp = req.AsyncGetResponse() , 현재 스레드는 포기되고 그 자원은 다른 용도로 사용될 수 있습니다. 응답이 1 초 후에 돌아 오면 스레드는이 1 초를 사용하여 다른 것들을 처리 할 수 ​​있습니다. 그렇지 않으면 스레드가 차단되어 스레드 자원을 1 초 동안 낭비합니다.

따라서 비동기 방식으로 병렬로 10000 개의 웹 페이지를 다운로드한다고하더라도 스레드 수는 적은 수로 제한됩니다.

나는 당신이 닷넷 / C # 프로그래머가 아니라고 생각한다. 비동기 튜토리얼은 대개 .Net과 C # (많은 코드)에서 비동기 IO를 프로그래밍하는 방법을 알고 있다고 가정합니다. F #에서 비동기 구문의 마술은 병렬을위한 것이 아닙니다. 왜냐하면 간단한 병렬은 다른 구조체, 예를 들어 ParallelFor에서 .Net 병렬 확장으로 구현 될 수 있기 때문입니다. 그러나 스레드가 실행을 포기하는 것을 볼 때 비동기 IO는 더 복잡합니다. IO가 끝나면 IO는 부모 스레드를 깨울 필요가 있습니다. 이것은 async magic이 사용되는 곳입니다. 여러 줄의 간결한 코드에서 매우 복잡한 제어를 할 수 있습니다.


최근 비동기 모듈의 기능에 대한 간단한 개요를 작성했습니다. 아마도 도움이 될 것입니다.





asynchronous