자바에서 코 루틴 구현
이 질문은 Java의 기존 코 루틴 구현에 대한 내 질문과 관련이 있습니다. 내가 의심하는 것처럼 현재 Java에서 사용할 수있는 코 루틴의 완전한 구현이없는 것으로 판명되면이를 구현하는 데 무엇이 필요합니까?
이 질문에서 말했듯이 다음 사항을 알고 있습니다.
- 백그라운드에서 스레드 / 스레드 풀로 "코 루틴"을 구현할 수 있습니다.
- 코 루틴을 가능하게하기 위해이면에서 JVM 바이트 코드로 까다로운 일을 할 수 있습니다.
- 소위 "Da Vinci Machine"JVM 구현에는 바이트 코드 조작없이 코 루틴을 실행할 수있는 기본 요소가 있습니다.
- 코 루틴에 대한 다양한 JNI 기반 접근 방식도 가능합니다.
차례로 각각의 결함을 해결하겠습니다.
스레드 기반 코 루틴
이 "해결책"은 병적입니다. 코 루틴의 요점은 스레딩, 잠금, 커널 스케줄링 등의 오버 헤드 를 피하는 것 입니다. 코 루틴은 가볍고 빠르며 사용자 공간에서만 실행되어야합니다. 엄격한 제한이있는 풀 틸트 스레드 측면에서 구현하면 모든 이점이 제거됩니다.
JVM 바이트 코드 조작
이 솔루션은 실행하기가 조금 어렵지만 더 실용적입니다. 이것은 C의 코 루틴 라이브러리에 대한 어셈블리 언어로 뛰어 드는 것과 거의 동일합니다 (그 중 몇 개가 작동하는지).
또한 비준수 스택에서 동일한 작업을 수행하는 방법을 찾을 수없는 한 완전히 호환되는 JVM 스택 (예 : Android 없음)에서만 코드를 실행하도록 연결합니다. 그러나이를 수행하는 방법을 찾으면 이제 시스템 복잡성과 테스트 요구 사항을 두 배로 늘린 것입니다.
다빈치 머신
Da Vinci Machine은 실험하기에는 멋지지만 표준 JVM이 아니기 때문에 모든 곳에서 기능을 사용할 수는 없습니다. 실제로 나는 대부분의 프로덕션 환경이 다빈치 머신의 사용을 특별히 금지 할 것이라고 생각합니다. 따라서 이것을 사용하여 멋진 실험을 할 수는 있지만 실제 세계에 출시 할 코드에는 사용할 수 없습니다.
또한 위의 JVM 바이트 코드 조작 솔루션과 유사한 추가 문제가 있습니다. Android와 같은 대체 스택에서는 작동하지 않습니다.
JNI 구현
이 솔루션은 Java에서이 작업을 수행하는 요점을 전혀 모의합니다. CPU와 운영 체제의 각 조합에는 독립적 인 테스트가 필요하며 각 조합은 잠재적으로 미묘한 실패의 지점입니다. 또는 물론 하나의 플랫폼에 내 자신을 묶을 수도 있지만 이것 역시 Java에서 작업을 수행한다는 점을 완전히 논란의 여지가 있습니다.
그래서...
이 네 가지 기술 중 하나를 사용하지 않고 Java에서 코 루틴을 구현하는 방법이 있습니까? 아니면 냄새가 가장 적은 네 가지 중 하나 (JVM 조작)를 대신 사용해야합니까?
추가하기 위해 편집 :
혼란이 포함되었는지 확인하기 위해 이것은 다른 질문 과 관련된 질문 이지만 동일하지는 않습니다. 그것은 불필요하게 바퀴를 재발 명하지 않기 위해 입찰에서 기존 구현을 찾고 있습니다. 이것은 다른 하나가 대답 할 수없는 것으로 판명 될 경우 Java에서 코 루틴을 구현하는 방법에 관한 질문입니다. 의도는 다른 스레드에 다른 질문을 유지하는 것입니다.
나는 이것을 살펴볼 것이다 : http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html , 꽤 흥미롭고 시작하기에 좋은 곳을 제공해야한다. 그러나 물론 우리는 Java를 사용하고 있으므로 더 잘할 수 있습니다 (또는 매크로가 없기 때문에 더 나쁠 수도 있습니다 :))
코 루틴에 대한 내 이해에서 일반적으로 생산자 와 소비자 코 루틴이 있습니다 (또는 적어도 이것이 가장 일반적인 패턴입니다). 그러나 의미 상 생산자가 소비자를 부르는 것을 원하지 않습니다. 이것은 비대칭을 도입하기 때문입니다. 그러나 스택 기반 언어가 작동하는 방식을 고려할 때 누군가 호출을해야합니다.
다음은 매우 간단한 유형 계층입니다.
public interface CoroutineProducer<T>
{
public T Produce();
public boolean isDone();
}
public interface CoroutineConsumer<T>
{
public void Consume(T t);
}
public class CoroutineManager
{
public static Execute<T>(CoroutineProducer<T> prod, CoroutineConsumer<T> con)
{
while(!prod.IsDone()) // really simple
{
T d = prod.Produce();
con.Consume(d);
}
}
}
물론 어려운 부분은 인터페이스를 구현 하는 것입니다. 특히 계산을 개별 단계로 나누는 것은 어렵습니다. 이를 위해 아마도 완전히 다른 영구 제어 구조 세트를 원할 것입니다 . 기본 아이디어는 제어의 로컬이 아닌 전송을 시뮬레이션하려는 것입니다 (결국 우리가 a를 시뮬레이션하는 것과 비슷합니다 goto
). 우리는 기본적으로 스택이 pc
아닌 힙에 현재 작업 상태를 유지함으로써 스택과 (프로그램 카운터) 를 사용하지 않으려 고합니다 . 따라서 많은 도우미 클래스가 필요합니다.
예를 들면 :
이상적인 세상에서 다음과 같은 소비자 (psuedocode)를 작성하고 싶다고 가정 해 보겠습니다.
boolean is_done;
int other_state;
while(!is_done)
{
//read input
//parse input
//yield input to coroutine
//update is_done and other_state;
}
is_done
and other_state
와 같은 지역 변수 를 추상화해야하며 yield
like 작업이 스택을 사용하지 않을 것이기 때문에 while 루프 자체를 추상화해야합니다 . 따라서 while 루프 추상화 및 관련 클래스를 만들어 보겠습니다.
enum WhileState {BREAK, CONTINUE, YIELD}
abstract class WhileLoop<T>
{
private boolean is_done;
public boolean isDone() { return is_done;}
private T rval;
public T getReturnValue() {return rval;}
protected void setReturnValue(T val)
{
rval = val;
}
public T loop()
{
while(true)
{
WhileState state = execute();
if(state == WhileState.YIELD)
return getReturnValue();
else if(state == WhileState.BREAK)
{
is_done = true;
return null;
}
}
}
protected abstract WhileState execute();
}
여기서 기본 트릭은 지역 변수를 클래스 변수 로 옮기고 범위 블록을 클래스로 바꾸어 반환 값을 산출 한 후 '루프'를 '다시 입력'할 수있는 기능을 제공하는 것입니다.
이제 제작자를 구현하려면
public class SampleProducer : CoroutineProducer<Object>
{
private WhileLoop<Object> loop;//our control structures become state!!
public SampleProducer()
{
loop = new WhileLoop()
{
private int other_state;//our local variables become state of the control structure
protected WhileState execute()
{
//this implements a single iteration of the loop
if(is_done) return WhileState.BREAK;
//read input
//parse input
Object calcluated_value = ...;
//update is_done, figure out if we want to continue
setReturnValue(calculated_value);
return WhileState.YIELD;
}
};
}
public Object Produce()
{
Object val = loop.loop();
return val;
}
public boolean isDone()
{
//we are done when the loop has exited
return loop.isDone();
}
}
다른 기본 제어 흐름 구조에 대해서도 유사한 트릭을 수행 할 수 있습니다. 이상적으로는 이러한 도우미 클래스의 라이브러리를 구축 한 다음이를 사용하여 궁극적으로 공동 루틴의 의미를 제공하는 이러한 간단한 인터페이스를 구현합니다. 여기에 쓴 모든 것이 일반화되고 크게 확장 될 수 있다고 확신합니다.
JVM 에서 Kotlin 코 루틴 을 살펴볼 것을 제안합니다 . 그러나 그것은 다른 범주에 속합니다. 관련된 바이트 코드 조작이 없으며 Android에서도 작동합니다. 그러나 Kotlin에서 코 루틴을 작성해야합니다. 장점은 Kotlin이 Java와의 상호 운용성을 염두에두고 설계 되었기 때문에 계속해서 모든 Java 라이브러리를 계속 사용할 수 있으며 동일한 프로젝트에서 Kotlin과 Java 코드를 자유롭게 결합 할 수 있습니다. 심지어 동일한 디렉토리에 나란히 배치하고 패키지.
이 kotlinx.coroutines 가이드 는 더 많은 예제를 제공하며 , 코 루틴 디자인 문서는 모든 동기, 사용 사례 및 구현 세부 사항을 설명합니다.
나는 방금이 질문을 보았고 C #과 비슷한 방식으로 코 루틴 또는 생성기를 구현할 수 있다고 생각합니다. 즉, 실제로 Java를 사용하지 않지만 CIL에는 JVM과 매우 유사한 제한이 있습니다.
C # 의 yield 문 은 순수한 언어 기능이며 CIL 바이트 코드의 일부가 아닙니다. C # 컴파일러는 각 생성기 함수에 대해 숨겨진 개인 클래스를 만듭니다. 함수에서 yield 문을 사용하는 경우 IEnumerator 또는 IEnumerable을 반환해야합니다. 컴파일러는 코드를 statemachine과 유사한 클래스로 "포장"합니다.
C # 컴파일러는 생성 된 코드에서 일부 "goto"를 사용하여 상태 머신으로 쉽게 변환 할 수 있습니다. 나는 자바 바이트 코드의 능력을 모르고 평범한 무조건 점프와 같은 것이 있다면 보통 "어셈블리 수준"에서 가능하다.
이미 언급했듯이이 기능은 컴파일러에서 구현되어야합니다. 저는 Java에 대한 지식이 거의없고 컴파일러이기 때문에 "전 처리기"등을 사용하여 컴파일러를 변경 / 확장 할 수 있는지 알 수 없습니다.
개인적으로 저는 코 루틴을 좋아합니다. Unity 게임 개발자로서 저는이 게임을 자주 사용합니다. ComputerCraft로 Minecraft를 많이 플레이하기 때문에 Lua (LuaJ)의 코 루틴이 스레드로 구현되는 이유가 궁금했습니다.
Kotlin은 코 루틴에 대해 다음 접근 방식을 사용합니다
( https://kotlinlang.org/docs/reference/coroutines.html ).
코 루틴은 컴파일 기술을 통해 완전히 구현되고 (VM 또는 OS 측의 지원이 필요하지 않음) 일시 중단은 코드 변환을 통해 작동합니다. 기본적으로 모든 일시 중단 기능 (최적화가 적용될 수 있지만 여기서는 다루지 않음)은 상태가 호출 일시 중단에 해당하는 상태 시스템으로 변환됩니다. 일시 중단 직전에 다음 상태는 관련 지역 변수 등과 함께 컴파일러 생성 클래스의 필드에 저장됩니다. 해당 코 루틴이 재개되면 지역 변수가 복원되고 상태 머신은 일시 중단 직후의 상태에서 진행됩니다.
일시 중단 된 코 루틴은 일시 중단 된 상태와 로컬을 유지하는 객체로 저장되고 전달 될 수 있습니다. 이러한 객체의 유형은 Continuation이며 여기에 설명 된 전체 코드 변환은 고전적인 Continuation-passing 스타일에 해당합니다. 결과적으로, 일시 중단 기능은 후드 아래에서 Continuation 유형의 추가 매개 변수를 취합니다.
https://github.com/Kotlin/kotlin-coroutines/blob/master/kotlin-coroutines-informal.md 에서 디자인 문서를 확인하십시오.
Java에서 사용하는 Coroutine 클래스가 있습니다. 스레드를 기반으로하며 스레드를 사용하면 병렬 작업이 가능하다는 장점이 있으며, 이는 멀티 코어 머신에서 이점이 될 수 있습니다. 따라서 스레드 기반 접근 방식을 고려할 수 있습니다.
Java6 +에 대한 또 다른 선택이 있습니다.
파이썬 코 루틴 구현 :
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
class CorRunRAII {
private final List<WeakReference<? extends CorRun>> resources = new ArrayList<>();
public CorRunRAII add(CorRun resource) {
if (resource == null) {
return this;
}
resources.add(new WeakReference<>(resource));
return this;
}
public CorRunRAII addAll(List<? extends CorRun> arrayList) {
if (arrayList == null) {
return this;
}
for (CorRun corRun : arrayList) {
add(corRun);
}
return this;
}
@Override
protected void finalize() throws Throwable {
super.finalize();
for (WeakReference<? extends CorRun> corRunWeakReference : resources) {
CorRun corRun = corRunWeakReference.get();
if (corRun != null) {
corRun.stop();
}
}
}
}
class CorRunYieldReturn<ReceiveType, YieldReturnType> {
public final AtomicReference<ReceiveType> receiveValue;
public final LinkedBlockingDeque<AtomicReference<YieldReturnType>> yieldReturnValue;
CorRunYieldReturn(AtomicReference<ReceiveType> receiveValue, LinkedBlockingDeque<AtomicReference<YieldReturnType>> yieldReturnValue) {
this.receiveValue = receiveValue;
this.yieldReturnValue = yieldReturnValue;
}
}
interface CorRun<ReceiveType, YieldReturnType> extends Runnable, Callable<YieldReturnType> {
boolean start();
void stop();
void stop(final Throwable throwable);
boolean isStarted();
boolean isEnded();
Throwable getError();
ReceiveType getReceiveValue();
void setResultForOuter(YieldReturnType resultForOuter);
YieldReturnType getResultForOuter();
YieldReturnType receive(ReceiveType value);
ReceiveType yield();
ReceiveType yield(YieldReturnType value);
<TargetReceiveType, TargetYieldReturnType> TargetYieldReturnType yieldFrom(final CorRun<TargetReceiveType, TargetYieldReturnType> another);
<TargetReceiveType, TargetYieldReturnType> TargetYieldReturnType yieldFrom(final CorRun<TargetReceiveType, TargetYieldReturnType> another, final TargetReceiveType value);
}
abstract class CorRunSync<ReceiveType, YieldReturnType> implements CorRun<ReceiveType, YieldReturnType> {
private ReceiveType receiveValue;
public final List<WeakReference<CorRun>> potentialChildrenCoroutineList = new ArrayList<>();
// Outside
private AtomicBoolean isStarted = new AtomicBoolean(false);
private AtomicBoolean isEnded = new AtomicBoolean(false);
private Throwable error;
private YieldReturnType resultForOuter;
@Override
public boolean start() {
boolean isStarted = this.isStarted.getAndSet(true);
if ((! isStarted)
&& (! isEnded())) {
receive(null);
}
return isStarted;
}
@Override
public void stop() {
stop(null);
}
@Override
public void stop(Throwable throwable) {
isEnded.set(true);
if (throwable != null) {
error = throwable;
}
for (WeakReference<CorRun> weakReference : potentialChildrenCoroutineList) {
CorRun child = weakReference.get();
if (child != null) {
child.stop();
}
}
}
@Override
public boolean isStarted() {
return isStarted.get();
}
@Override
public boolean isEnded() {
return isEnded.get();
}
@Override
public Throwable getError() {
return error;
}
@Override
public ReceiveType getReceiveValue() {
return receiveValue;
}
@Override
public void setResultForOuter(YieldReturnType resultForOuter) {
this.resultForOuter = resultForOuter;
}
@Override
public YieldReturnType getResultForOuter() {
return resultForOuter;
}
@Override
public synchronized YieldReturnType receive(ReceiveType value) {
receiveValue = value;
run();
return getResultForOuter();
}
@Override
public ReceiveType yield() {
return yield(null);
}
@Override
public ReceiveType yield(YieldReturnType value) {
resultForOuter = value;
return receiveValue;
}
@Override
public <TargetReceiveType, TargetYieldReturnType> TargetYieldReturnType yieldFrom(CorRun<TargetReceiveType, TargetYieldReturnType> another) {
return yieldFrom(another, null);
}
@Override
public <TargetReceiveType, TargetYieldReturnType> TargetYieldReturnType yieldFrom(CorRun<TargetReceiveType, TargetYieldReturnType> another, TargetReceiveType value) {
if (another == null || another.isEnded()) {
throw new RuntimeException("Call null or isEnded coroutine");
}
potentialChildrenCoroutineList.add(new WeakReference<CorRun>(another));
synchronized (another) {
boolean isStarted = another.start();
boolean isJustStarting = ! isStarted;
if (isJustStarting && another instanceof CorRunSync) {
return another.getResultForOuter();
}
return another.receive(value);
}
}
@Override
public void run() {
try {
this.call();
}
catch (Exception e) {
e.printStackTrace();
stop(e);
return;
}
}
}
abstract class CorRunThread<ReceiveType, YieldReturnType> implements CorRun<ReceiveType, YieldReturnType> {
private final ExecutorService childExecutorService = newExecutorService();
private ExecutorService executingOnExecutorService;
private static final CorRunYieldReturn DUMMY_COR_RUN_YIELD_RETURN = new CorRunYieldReturn(new AtomicReference<>(null), new LinkedBlockingDeque<AtomicReference>());
private final CorRun<ReceiveType, YieldReturnType> self;
public final List<WeakReference<CorRun>> potentialChildrenCoroutineList;
private CorRunYieldReturn<ReceiveType, YieldReturnType> lastCorRunYieldReturn;
private final LinkedBlockingDeque<CorRunYieldReturn<ReceiveType, YieldReturnType>> receiveQueue;
// Outside
private AtomicBoolean isStarted = new AtomicBoolean(false);
private AtomicBoolean isEnded = new AtomicBoolean(false);
private Future<YieldReturnType> future;
private Throwable error;
private final AtomicReference<YieldReturnType> resultForOuter = new AtomicReference<>();
CorRunThread() {
executingOnExecutorService = childExecutorService;
receiveQueue = new LinkedBlockingDeque<>();
potentialChildrenCoroutineList = new ArrayList<>();
self = this;
}
@Override
public void run() {
try {
self.call();
}
catch (Exception e) {
stop(e);
return;
}
stop();
}
@Override
public abstract YieldReturnType call();
@Override
public boolean start() {
return start(childExecutorService);
}
protected boolean start(ExecutorService executorService) {
boolean isStarted = this.isStarted.getAndSet(true);
if (!isStarted) {
executingOnExecutorService = executorService;
future = (Future<YieldReturnType>) executingOnExecutorService.submit((Runnable) self);
}
return isStarted;
}
@Override
public void stop() {
stop(null);
}
@Override
public void stop(final Throwable throwable) {
if (throwable != null) {
error = throwable;
}
isEnded.set(true);
returnYieldValue(null);
// Do this for making sure the coroutine has checked isEnd() after getting a dummy value
receiveQueue.offer(DUMMY_COR_RUN_YIELD_RETURN);
for (WeakReference<CorRun> weakReference : potentialChildrenCoroutineList) {
CorRun child = weakReference.get();
if (child != null) {
if (child instanceof CorRunThread) {
((CorRunThread)child).tryStop(childExecutorService);
}
}
}
childExecutorService.shutdownNow();
}
protected void tryStop(ExecutorService executorService) {
if (this.executingOnExecutorService == executorService) {
stop();
}
}
@Override
public boolean isEnded() {
return isEnded.get() || (
future != null && (future.isCancelled() || future.isDone())
);
}
@Override
public boolean isStarted() {
return isStarted.get();
}
public Future<YieldReturnType> getFuture() {
return future;
}
@Override
public Throwable getError() {
return error;
}
@Override
public void setResultForOuter(YieldReturnType resultForOuter) {
this.resultForOuter.set(resultForOuter);
}
@Override
public YieldReturnType getResultForOuter() {
return this.resultForOuter.get();
}
@Override
public YieldReturnType receive(ReceiveType value) {
LinkedBlockingDeque<AtomicReference<YieldReturnType>> yieldReturnValue = new LinkedBlockingDeque<>();
offerReceiveValue(value, yieldReturnValue);
try {
AtomicReference<YieldReturnType> takeValue = yieldReturnValue.take();
return takeValue == null ? null : takeValue.get();
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
@Override
public ReceiveType yield() {
return yield(null);
}
@Override
public ReceiveType yield(final YieldReturnType value) {
returnYieldValue(value);
return getReceiveValue();
}
@Override
public <TargetReceiveType, TargetYieldReturnType> TargetYieldReturnType yieldFrom(final CorRun<TargetReceiveType, TargetYieldReturnType> another) {
return yieldFrom(another, null);
}
@Override
public <TargetReceiveType, TargetYieldReturnType> TargetYieldReturnType yieldFrom(final CorRun<TargetReceiveType, TargetYieldReturnType> another, final TargetReceiveType value) {
if (another == null || another.isEnded()) {
throw new RuntimeException("Call null or isEnded coroutine");
}
boolean isStarted = false;
potentialChildrenCoroutineList.add(new WeakReference<CorRun>(another));
synchronized (another) {
if (another instanceof CorRunThread) {
isStarted = ((CorRunThread)another).start(childExecutorService);
}
else {
isStarted = another.start();
}
boolean isJustStarting = ! isStarted;
if (isJustStarting && another instanceof CorRunSync) {
return another.getResultForOuter();
}
TargetYieldReturnType send = another.receive(value);
return send;
}
}
@Override
public ReceiveType getReceiveValue() {
setLastCorRunYieldReturn(takeLastCorRunYieldReturn());
return lastCorRunYieldReturn.receiveValue.get();
}
protected void returnYieldValue(final YieldReturnType value) {
CorRunYieldReturn<ReceiveType, YieldReturnType> corRunYieldReturn = lastCorRunYieldReturn;
if (corRunYieldReturn != null) {
corRunYieldReturn.yieldReturnValue.offer(new AtomicReference<>(value));
}
}
protected void offerReceiveValue(final ReceiveType value, LinkedBlockingDeque<AtomicReference<YieldReturnType>> yieldReturnValue) {
receiveQueue.offer(new CorRunYieldReturn(new AtomicReference<>(value), yieldReturnValue));
}
protected CorRunYieldReturn<ReceiveType, YieldReturnType> takeLastCorRunYieldReturn() {
try {
return receiveQueue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
protected void setLastCorRunYieldReturn(CorRunYieldReturn<ReceiveType,YieldReturnType> lastCorRunYieldReturn) {
this.lastCorRunYieldReturn = lastCorRunYieldReturn;
}
protected ExecutorService newExecutorService() {
return Executors.newCachedThreadPool(getThreadFactory());
}
protected ThreadFactory getThreadFactory() {
return new ThreadFactory() {
@Override
public Thread newThread(final Runnable runnable) {
Thread thread = new Thread(runnable);
thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
throwable.printStackTrace();
if (runnable instanceof CorRun) {
CorRun self = (CorRun) runnable;
self.stop(throwable);
thread.interrupt();
}
}
});
return thread;
}
};
}
}
이제 이런 식으로 파이썬 코 루틴을 사용할 수 있습니다 (예 : 피보나치 수)
스레드 버전 :
class Fib extends CorRunThread<Integer, Integer> {
@Override
public Integer call() {
Integer times = getReceiveValue();
do {
int a = 1, b = 1;
for (int i = 0; times != null && i < times; i++) {
int temp = a + b;
a = b;
b = temp;
}
// A pythonic "yield", i.e., it returns `a` to the caller and waits `times` value from the next caller
times = yield(a);
} while (! isEnded());
setResultForOuter(Integer.MAX_VALUE);
return getResultForOuter();
}
}
class MainRun extends CorRunThread<String, String> {
@Override
public String call() {
// The fib coroutine would be recycled by its parent
// (no requirement to call its start() and stop() manually)
// Otherwise, if you want to share its instance and start/stop it manually,
// please start it before being called by yieldFrom() and stop it in the end.
Fib fib = new Fib();
String result = "";
Integer current;
int times = 10;
for (int i = 0; i < times; i++) {
// A pythonic "yield from", i.e., it calls fib with `i` parameter and waits for returned value as `current`
current = yieldFrom(fib, i);
if (fib.getError() != null) {
throw new RuntimeException(fib.getError());
}
if (current == null) {
continue;
}
if (i > 0) {
result += ",";
}
result += current;
}
setResultForOuter(result);
return result;
}
}
동기화 (비 스레드) 버전 :
class Fib extends CorRunSync<Integer, Integer> {
@Override
public Integer call() {
Integer times = getReceiveValue();
int a = 1, b = 1;
for (int i = 0; times != null && i < times; i++) {
int temp = a + b;
a = b;
b = temp;
}
yield(a);
return getResultForOuter();
}
}
class MainRun extends CorRunSync<String, String> {
@Override
public String call() {
CorRun<Integer, Integer> fib = null;
try {
fib = new Fib();
} catch (Exception e) {
e.printStackTrace();
}
String result = "";
Integer current;
int times = 10;
for (int i = 0; i < times; i++) {
current = yieldFrom(fib, i);
if (fib.getError() != null) {
throw new RuntimeException(fib.getError());
}
if (current == null) {
continue;
}
if (i > 0) {
result += ",";
}
result += current;
}
stop();
setResultForOuter(result);
if (Utils.isEmpty(result)) {
throw new RuntimeException("Error");
}
return result;
}
}
실행 (두 버전 모두 작동) :
// Run the entry coroutine
MainRun mainRun = new MainRun();
mainRun.start();
// Wait for mainRun ending for 5 seconds
long startTimestamp = System.currentTimeMillis();
while(!mainRun.isEnded()) {
if (System.currentTimeMillis() - startTimestamp > TimeUnit.SECONDS.toMillis(5)) {
throw new RuntimeException("Wait too much time");
}
}
// The result should be "1,1,2,3,5,8,13,21,34,55"
System.out.println(mainRun.getResultForOuter());
또한이 퀘이사 Java 및 대한 프로젝트 직조기 확장 섬유와 연속성을위한 JVM에 만들어진 오라클은. 여기입니다 직기의 프리젠 테이션 Youtoube에가. 몇 가지 더 있습니다. 약간의 검색으로 쉽게 찾을 수 있습니다.
참고 URL : https://stackoverflow.com/questions/2846664/implementing-coroutines-in-java
'program story' 카테고리의 다른 글
QThread를 구현하는 올바른 방법은 무엇입니까… (예제를보십시오…) (0) | 2020.12.02 |
---|---|
.NET 응용 프로그램의 비정상적인 잘못된 Viewstate 문제 (0) | 2020.12.02 |
JavaScript가 브라우저에 구현 된 유일한 클라이언트 측 스크립팅 언어 인 이유는 무엇입니까? (0) | 2020.12.02 |
Visual Studio 2012 및 Entity Framework 5에서 단위 테스트를 위해 LocalDb를 설정하는 방법 (0) | 2020.12.02 |
rdtscp, rdtsc : 메모리와 CPUID / rdtsc의 차이점은 무엇입니까? (0) | 2020.12.02 |