태그 보관물: asynchronous-programming

asynchronous-programming

프로그래밍 언어가 동기 / 비동기 문제를 자동으로 관리하지 않는 이유는 무엇입니까? (nbOfUsers) { console.log(nbOfUsers) }); 다음과 같이 쓸

나는 이것에 관한 많은 자료를 찾지 못했다. 동기 방식으로 비동기 코드를 작성할 수 있는지 / 좋은 아이디어인지 궁금했다.

예를 들어, 데이터베이스에 저장된 사용자 수를 검색하는 일부 JavaScript 코드는 다음과 같습니다 (비동기 작업).

getNbOfUsers(function (nbOfUsers) { console.log(nbOfUsers) });

다음과 같이 쓸 수 있으면 좋을 것입니다.

const nbOfUsers = getNbOfUsers();
console.log(getNbOfUsers);

따라서 컴파일러는 자동으로 응답을 기다렸다가 실행 console.log합니다. 결과가 다른 곳에서 사용되기 전에 항상 비동기 작업이 완료 될 때까지 대기합니다. 콜백 약속, 비동기 / 대기 등을 훨씬 덜 사용하며 작업 결과가 즉시 제공되는지 여부를 걱정할 필요가 없습니다.

nbOfUserstry / catch 또는 Swift 언어 와 같은 선택 사항을 사용하여 오류를 여전히 관리 가능합니다 ( 정수 또는 오류가 발생 했습니까?) .

가능합니까? 끔찍한 생각 일 수도 있고 유토피아 일 수도 있습니다 … 잘 모르겠습니다.



답변

Async / await는 두 개의 추가 키워드가 있지만 제안한 자동화 된 관리입니다. 왜 중요한가요? 이전 버전과의 호환성 외에도?

  • 코 루틴이 중단되고 재개 될 수있는 명백한 지점이 없다면 , 기다릴 수있는 값을 기다려야하는 곳을 감지하기 위한 유형 시스템 이 필요 합니다. 많은 프로그래밍 언어에는 이러한 유형 시스템이 없습니다.

  • 값을 기다리는 것을 명시 적으로함으로써, 우리는 일류 객체로서의 값을 약속 할 수 있습니다. 이것은 고차 코드를 작성할 때 매우 유용 할 수 있습니다.

  • 비동기 코드는 언어의 예외 유무와 유사하게 언어의 실행 모델에 매우 깊은 영향을 미칩니다. 특히, 비동기 기능은 비동기 기능으로 만 대기 할 수 있습니다. 이것은 모든 호출 기능에 영향을 미칩니다! 그러나이 의존성 체인의 끝에서 함수를 비동기가 아닌 비동기에서 비동기로 변경하면 어떻게 될까요? 모든 기능이 비동기 적이며 모든 기능 호출이 기본적으로 대기하지 않는 한 이전 버전과 호환되지 않는 변경입니다.

    이는 성능에 매우 나쁜 영향을 미치기 때문에 바람직하지 않습니다. 값싼 값을 단순히 반환 할 수는 없습니다. 모든 함수 호출은 훨씬 비싸 질 것입니다.

비동기는 훌륭하지만 어떤 종류의 암시 적 비동기는 실제로 작동하지 않습니다.

Haskell과 같은 순수 기능 언어는 실행 순서가 크게 지정되지 않고 관찰 할 수 없기 때문에 약간의 탈출구가 있습니다. 또는 다르게 표현 : 특정 작업 순서는 명시 적으로 인코딩해야합니다. 실제 프로그램, 특히 비동기 코드가 매우 적합한 I / O가 많은 프로그램에서는 다소 번거로울 수 있습니다.


답변

누락 된 것은 비동기 작업 의 목적 입니다. 대기 시간을 활용할 수 있습니다!

서버에서 일부 리소스 요청과 같은 비동기 작업을 암시 적으로 즉시 응답을 대기하여 동기 작업으로 설정하면 스레드가 대기 시간으로 다른 작업을 수행 할 수 없습니다 . 서버가 응답하는 데 10 밀리 초가 걸리면 약 3 천만 개의 CPU주기가 낭비됩니다. 응답 대기 시간이 요청의 실행 시간이됩니다.

프로그래머가 비동기 작업을 발명 한 유일한 이유 는 다른 유용한 계산 뒤에 본질적으로 오래 실행되는 작업의 대기 시간을 숨기는 것 입니다. 유용한 작업으로 대기 시간을 채울 수 있으면 CPU 시간이 절약됩니다. 당신이 할 수 없다면, 비동기 작업으로 인해 손실되는 것이 없습니다.

따라서 귀하의 언어가 귀하에게 제공하는 비동기 작업을 수용하는 것이 좋습니다. 그들은 당신을 시간을 절약하기 위해 있습니다.


답변

일부는 그렇습니다.

비동기식은 비교적 새로운 기능이기 때문에 주류가 아닙니다. 아마도 지금은 좋은 기능인지 또는 친숙하고 사용 가능한 방식으로 프로그래머에게 제공하는 방법에 대해서만 좋은 느낌을 얻은 비교적 새로운 기능이기 때문입니다. 표현 등 기존 비동기 기능은 기존 언어에 크게 고정되어 있으므로 약간 다른 디자인 방식이 필요합니다.

즉, 모든 곳에서하는 것이 분명 좋은 생각은 아닙니다 . 일반적인 실패는 루프에서 비동기 호출을 수행하여 효과적으로 실행을 직렬화하는 것입니다. 비동기 호출이 암시 적으로 있으면 이러한 종류의 오류가 모호해질 수 있습니다. 또한 Task<T>(또는 귀하의 언어와 동등한) 에서로 암시 적 강제를 지원 T하는 경우, 프로그래머가 실제로 원하는 두 가지 중 어느 것이 확실하지 않은 경우 유형 검사기 및 오류보고에 약간의 복잡성 / 비용이 추가 될 수 있습니다.

그러나 이것들은 극복 할 수없는 문제가 아닙니다. 당신이 그 행동을 지원하기를 원한다면 당신은 거의 확실하게 할 수 있지만, 절충이있을 것입니다.


답변

이를 수행하는 언어가 있습니다. 그러나 기존 언어 기능으로 쉽게 수행 할 수 있기 때문에 실제로는 그다지 필요하지 않습니다.

만큼 당신이 가지고있는 몇 가지 비동기를 표현하는 방법을, 당신은 구현할 수있는 선물 또는 약속을 순수 라이브러리 기능으로, 당신은 특별한 언어 기능이 필요하지 않습니다. 그리고만큼 당신이 가지고있는 몇 가지 표현의 투명 프록시를 , 당신은 함께 두 가지 기능을 넣을 수 있습니다 그리고 당신은 투명 선물을 .

예를 들어, 스몰 토크와 그 자손들에서, 객체는 그것의 동일성을 바꿀 수 있고, 문자 그대로 다른 객체를 “나올”수 있습니다 (실제로 이것을 수행하는 방법을이라고합니다 Object>>become:).

을 반환하는 장기 실행 계산을 상상해보십시오 Future<Int>. 이것은 Future<Int>모든 동일한 방법 갖는 Int다른 구현 예를 제외. Future<Int>+방법은 다른 숫자를 추가하지 않고 결과를 반환하며 Future<Int>계산을 래핑 하는 새로운 값 을 반환합니다 . 등등. 현명를 반환하여 구현 될 수없는 방법 Future<Int>대신 자동으로됩니다 await결과는 다음 호출 self become: result.하는이 (현재 실행중인 개체를 만들 것 self, 즉이 Future<Int>) 그대로가 resulta가 될하는 데 사용하는 객체 참조 지금부터 즉, 객체 Future<Int>입니다 이제는 Int클라이언트에게 완전히 투명한 곳입니다.

특별한 비동기 성 관련 언어 기능이 필요하지 않습니다.


답변

그들은 (잘, 대부분) 있습니다. 찾고있는 기능을 스레드 라고 합니다 .

그러나 스레드에는 자체 문제가 있습니다.

  1. 코드가에 중단 될 수 있기 때문에 어느 시점 , 당신은 할 수없는 지금 그 일이 “스스로”변경되지 않습니다 가정합니다. 스레드를 사용하여 프로그래밍 할 때 프로그램 변경 사항을 처리하는 방법에 대해 많은 시간을 낭비합니다.

    게임 서버가 다른 플레이어에 대한 플레이어의 공격을 처리한다고 가정합니다. 이 같은:

    if (playerInMeleeRange(attacker, victim)) {
        const damage = calculateAttackDamage(attacker, victim);
        if (victim.health <= damage) {
    
            // attacker gets whatever the victim was carrying as loot
            const loot = victim.getInventoryItems();
            attacker.addInventoryItems(loot);
            victim.removeInventoryItems(loot);
    
            victim.sendMessage("${attacker} hits you with a ${attacker.currentWeapon} and you die!");
            victim.setDead();
        } else {
            victim.health -= damage;
            victim.sendMessage("${attacker} hits you with a ${attacker.currentWeapon}!");
        }
        attacker.markAsKiller();
    }
    

    3 개월 후, 플레이어 attacker.addInventoryItems는 도망 칠 때 정확하게 죽이고 로그 오프 victim.removeInventoryItems하면 실패 할 것이며, 아이템을 보관할 수 있으며 공격자는 아이템의 사본을 얻는다는 것을 알게됩니다. 그는 이것을 여러 차례해서 얇은 공기에서 백만 톤의 금을 만들어 게임 경제를 무너 뜨 렸습니다.

    또는 게임에서 피해자에게 메시지를 보내는 동안 공격자가 로그 아웃 할 수 있으며, 머리 위에 “살인자”태그가 표시되지 않으므로 다음 피해자가 도망 가지 않습니다.

  2. 코드는 언제 라도 일시 중단 될 수 있으므로 데이터 구조를 조작 할 때는 모든 곳에서 잠금을 사용해야합니다. 나는 게임에서 명백한 결과를 가져 오는 위의 예를 들었지만 더 미묘 할 수 있습니다. 연결된 목록의 시작 부분에 항목을 추가하십시오.

    newItem.nextItem = list.firstItem;
    list.firstItem = newItem;
    

    스레드가 I / O를 수행 할 때만 일시 중단 될 수 있으며 어떤 시점에서도 중단되지 않는다고 말하는 경우에는 문제가되지 않습니다. 그러나 로깅과 같은 I / O 작업이있는 상황을 상상할 수 있습니다.

    for (player = playerList.firstItem; player != null; player = item.nextPlayer) {
        debugLog("${item.name} is online, they get a gold star");
        // Oops! The player might've logged out while the log message was being written to disk, and now this will throw an exception and the remaining players won't get their gold stars.
        // Or the list might've been rearranged and some players might get two and some players might get none.
        player.addInventoryItem(InventoryItems.GoldStar);
    }
    
  3. 코드는 언제든 일시 중단 될 수 있으므로 저장해야 할 상태가 많이있을 수 있습니다. 시스템은 각 스레드에 완전히 별도의 스택을 제공하여이를 처리합니다. 그러나 스택은 상당히 커서 32 비트 프로그램에서 약 2000 개 이상의 스레드를 가질 수 없습니다. 또는 스택 크기를 너무 작게 만들 위험이 있으므로 스택 크기를 줄일 수 있습니다.


답변

질문이 문자 그대로 비 블로킹 IO가 아닌 비동기 프로그래밍에 대해 묻는 것이지만이 특별한 경우에는 다른 것에 대해 논의하지 않고 하나를 논의 할 수는 없다고 생각하기 때문에 오해의 소지가있는 많은 답변을 찾습니다.

비동기식 프로그래밍은 본질적으로 비동기식이지만 비동기식 프로그래밍의 단점은 대부분 커널 스레드를 막는 것입니다. Node.js는 콜백 또는 Promises를 통해 비동기 성을 사용 하여 블로킹 작업이 이벤트 루프에서 전달되도록하고 Java의 Netty는 콜백 또는 CompletableFutures를 통해 비동기 성을 사용 하여 유사한 작업을 수행합니다.

그러나 비 블로킹 코드는 비동기 성을 요구하지 않습니다 . 프로그래밍 언어와 런타임이 당신을 위해 얼마나 기꺼이 할 것인지에 달려 있습니다.

Go, Erlang 및 Haskell / GHC가이를 처리 할 수 ​​있습니다. var response = http.get('example.com/test')응답을 기다리는 동안 같은 것을 작성 하고 장면 뒤에서 커널 스레드를 해제하도록 할 수 있습니다 . 이것은 goroutines, Erlang 프로세스 또는 forkIO블로킹 할 때 씬 뒤에 커널 스레드 를 놓아서 응답을 기다리는 동안 다른 작업을 수행함으로써 수행됩니다.

언어가 실제로 비동기 성을 처리 할 수는 없지만 일부 추상화는 무제한 연속 또는 비대칭 코 루틴과 같은 다른 것보다 더 나아갈 수 있습니다. 그러나 시스템 호출을 차단하는 비동기 코드의 주요 원인은 개발자로부터 완전히 추상화 될 있습니다.

Node.js와 Java는 비동기 비 차단 코드를 지원하는 반면 Go와 Erlang은 동기 비 차단 코드를 지원 합니다 . 그것들은 서로 다른 트레이드 오프를 가진 유효한 접근 방식입니다.

오히려 주관적인 주장은 개발자를 대신하여 비 블로킹을 관리하는 런타임에 대해 논쟁하는 사람들은 초기의 가비지 수집에 대해 논쟁하는 사람들과 같다는 것입니다. 예, 비용이 발생하지만 (이 경우 주로 더 많은 메모리) 개발 및 디버깅이 쉬워지고 코드베이스가 더욱 강력 해집니다.

개인적으로 비동기 비 차단 코드는 향후 시스템 프로그래밍을 위해 예약해야하고보다 현대적인 기술 스택은 응용 프로그램 개발을 위해 동기 비 차단 런타임으로 마이그레이션해야한다고 주장합니다 .


답변

내가 당신을 올바르게 읽으면 동기식 프로그래밍 모델을 요구하지만 고성능 구현을 요구합니다. 그것이 맞다면 Erlang 또는 Haskell과 같은 녹색 스레드 또는 프로세스 형태로 이미 사용 가능합니다. 그렇습니다. 훌륭한 아이디어이지만 기존 언어에 대한 개장이 항상 원하는만큼 매끄럽지는 않을 수 있습니다.