3

일부 코드를 상속 받았으며 원래 개발자가 남긴 사람이 없습니다. 이 코드는 주로 CompletableFuture을 사용하며 처음 사용하기 때문에 계속 머리를 감싸려고 노력하고 있습니다. 내가 이해할 때, (Completable)Future은 일반적으로 시간이 많이 걸리는 작업이 실행되는 동안 우리가 다른 일을 할 수있게 해주는 몇 가지 멀티 스레딩 메커니즘과 함께 사용되며, 그 다음 단순히 미래를 통해 그 결과를 가져온다.멀티 스레딩없이 Future를 사용하는 이유는 무엇입니까?

interface ArchiveSearcher { String search(String target); } 
class App { 
    ExecutorService executor = ... 
    ArchiveSearcher searcher = ... 
    void showSearch(final String target) throws InterruptedException { 
     Future<String> future = executor.submit(new Callable<String>() { 
     public String call() { 
      return searcher.search(target); 
     }}); 
     displayOtherThings(); // do other things while searching 
     try { 
      displayText(future.get()); // use future 
     } catch (ExecutionException ex) { cleanup(); return; } 
    } 
} 

그러나, 나는 상속 한이 응용 프로그램에서, 어떤 멀티 스레딩을 사용하지 않는 다음과 같은 패턴이 배의 무리 나타납니다 : the javadoc에서와 같이 나에게

public Object serve(Object input) throws ExecutionException, InterruptedException { 
    CompletableFuture<Object> result = delegate1(input); 
    return result.get(); 
} 

private CompletableFuture<Object> delegate1(Object input) { 
    // Do things 
    return delegate2(input); 
} 

private CompletableFuture<Object> delegate2(Object input) { 
    return CompletableFuture.completedFuture(new Object()); 
} 

을,이다 상당 : 물론

public Object serve(Object input) { 
    Object result = delegate1(input); 
    return result; 
} 

private Object delegate1(Object input) { 
    // Do things 
    return delegate2(input); 
} 

private Object delegate2(Object input) { 
    return new Object(); 
} 

코드가 훨씬 더 복잡하고, 오류가 발생하는 경우에 exceptionallyCompletedFuture을 반환하지만 Callable이다가, 아니 Runnable, 아니 Executor, 아니 supplyAsync() 멀티 스레딩의 흔적. 내가 뭘 놓치고 있니? 단일 스레드 컨텍스트에서 Future을 사용하는 이유는 무엇입니까?

+0

UI 관련 작업이 있습니까? 주 스레드가 사물을 계산하는 경우 속도가 느려집니다. –

+0

@ChristianKuetbach 아니, 없습니다. 하지만 만약 있다면? "메인 스레드"는 무엇을 의미합니까? 내 요점은, 내 이해에, 위의 접근 방식으로 모든 것이 "주요 스레드"입니다. – mark951131b

+0

나는 안드로이드 개발자가 아니지만 "메인 스레드"가 내가 보통 메인 스레드라고 생각하는 것과 다른 것을 의미하는 것 같은 안드로이드 질문을 많이 보아왔다. Swing의 Event Dispatch Thread와 같은 것입니다. –

답변

0

ExecutorService 구현은 일반적으로 스레드를 관리합니다. 나는 정확히 그것을하는 ThreadPoolExecutor를 사용했다. 코드에서 사용하는 ExecutorService를 주석 처리했습니다.

+3

'ExecutorService'를 사용하는 코드 스 니펫은 반례입니다 - OP_does_가 이해하는 어떤 것의 예입니다. 실제 질문은 두 번째 발췌 문장에서'CompletableFuture.completedFuture (...)'호출에 관한 것입니다. –

+0

@ jameslarge 바로 고마워요. – mark951131b

1

예, 현재로서는 해당 코드에 멀티 스레딩이 사용되지 않았습니다. 보이는 개발자가 나중에 멀티 스레딩을 사용하기로 결정하는 경우 만

delegate2() 

방법을 수정해야하는 방식으로 단일 스레드 코드를 작성하는 의도가 있었다처럼.

2

미래는 비동기 프로그래밍이있는 상황에서 중요합니다. 비동기 프로그래밍의 가장 큰 장점 중 하나는 단일 스레드로 매우 효율적인 코드를 작성할 수 있다는 것입니다.

또한 선물은 모두 또는 모든 제안이되는 경향이 있습니다. 비동기 코드를 작성하려면 모든 메소드가 비동기적인 작업을 수행하지는 않더라도 위에서 아래로 수행해야합니다.

예를 들어, twisted 또는 express과 같은 단일 스레드 HTTP 서버를 작성하려는 경우를 고려하십시오.

while (true) { 
    if (serverSocket.ready()) { 
    connection = serverSocket.accept(); 
    futures.add(server.serve(connection)); 
    } 
    for (Future future : futures) { 
    if (future.isDone()) { 
     Object result = future.get(); 
     sendResult(result); 
    } 
    } 
    //Some kind of select-style wait here 
} 

은 하나의 스레드가 있지만 시간이 작업이 그 일반적으로 읽기, 데이터베이스, 파일 읽기 (대기를 필요로 발생합니다 서버 (여기서는 매우 자유 의사)의 최상위 수준과 같을 수 있습니다 요청 등) 그것은 미래를 사용하며 하나의 스레드를 차단하지 않으므로 고성능 단일 스레드 HTTP 서버를 갖습니다.

이제 응용 프로그램의 최상위 수준이 위와 같고 어떤 시점에서 매우 낮은 수준의 요청이 파일에서 무엇인가를 읽어야한다면 어떻게 될지 상상해보십시오. 그 파일을 읽으면 미래가 생길 것입니다. 중간에있는 모든 레이어가 미래를 처리하지 못하면 차단해야하며 목적을 무너 뜨릴 수 있습니다. 이것이 내가 선물이 전부 아니면 전혀없는 경향이 있다고 말하는 이유입니다.

그래서 내 생각은 다음 중 하나입니다

  1. 당신의 친구가 현재 어떤 비동기을 수행하고 당신이 그렇다면, 그가 차단 아직 (그 사람이 파일이나 데이터베이스 또는 아무것도 읽을 수행을 잡은하지 않은?).
  2. 언젠가는 비동기식 작업을 계획하고 있으며 계획을 세우고 싶었습니다.
  3. 그는 다른 비동기 프레임 워크에서 많은 시간을 보냈지 만 올바르게 사용하지 않아도 스타일을 좋아하게되었습니다.
0

비동기 코드의 핵심은 연속 코드를 연기하는 것입니다.

가장 일반적인 시나리오는 I/O입니다. 작업이 끝나기를 기다리는 대신 "할 일을하고 내가 끝나면 알려줍니다"또는 더 일반적으로 "할 일을하고 이렇게하십시오 당신이 끝나면 ".

이것은 스레드를 전혀 의미하지 않습니다. 네트워크 카드 나 하드 드라이브 등 어떤 장치에서든 읽는 것은 일반적으로 장치에서 CPU로 보내는 신호 나 인터럽트를 가지고 있습니다. 그 동안 CPU를 사용할 수 있습니다. "notify me"는 디스패치 루프 또는 스케줄러를 구현하는 하위 레벨 코드에서 더 일반적입니다. "do this"는 더 높은 수준의 코드에서 더 일반적입니다.이 코드에서는 사용자에게 파견 및/또는 일정을 수립 할 수있는 확립 된 라이브러리 또는 프레임 워크를 사용합니다.

덜 일반적인 시나리오는 스레드를 차단하지 않고 (타이머를 생각하면 대 Thread.sleep()) 실행을 연기하고 작업을 분할하는 것입니다. 사실, 분할 작업은 여러 스레드에서 매우 일반적입니다. 즉, 약간의 오버 헤드를 사용하여 성능을 향상시킬 수 있지만 오버 헤드가 단순한 오버 헤드 인 단일 스레드에서는 그렇지 않습니다.


방금 ​​성공적으로 또는 매우, 정말 비동기없는 비동기 코드의 오버 헤드의 일부인지 여부, CompletableFuture의 완료 빌드 예제로 제공하는 코드입니다. 즉, 결과를 즉시 제공 할 수있는 경우에도 정의 된 비동기 스타일을 따라야합니다.이 경우에는 결과에 대해 소량의 메모리 할당이 필요합니다.

이것은 수천 개의 호출 또는 초당 수십 개의 스레드를 가진 수십 개의 호출에서 눈에 띄게됩니다.

경우에 따라 예를 들어 미리 정의 된 선물을 가지고 최적화 할 수 있습니다. null, 0, 1, -1, 빈 배열/목록/스트림 또는 기타 매우 일반적이거나 고정 된 결과 일 수 있습니다. 비슷한 접근법은 결과가 같지 않은 래핑 결과를 캐시하는 것입니다. 그러나이 방법을 사용하기 전에 먼저 프로필을 제안하십시오. 병목 현상이 아닌 것을 조기에 최적화 할 수 있습니다.

관련 문제