私は、それぞれが特定のタスクに必要な、いくつかの非同期のメソッドを互いに呼び出す多くのメソッドを持っています。それらはすべて DOM で動作します (したがって、いつでも 1 つのスレッドだけが DOM にアクセスする必要があります)。
例えば:
object A() {
/*...A() code 1...*/
var res = B();
/*...A() code 2 that uses res...*/
}
object B() {
/*...B code 1...*/
var res1 = C();
/*...B code 2 that uses res1...*/
var res2 = C();
/*...B code 3 that uses res2...*/
}
object C() {
/*...C code 1...*/
if (rnd.NextDouble() < 0.3) { // unpredictable condition
startAsyncStuff();
/*...C code 2 that uses async result above...*/
}
if (rnd.NextDouble() < 0.7) { // unpredictable condition
startOtherAsyncStuff();
/*...C code 3 that might use any/both async results above...*/
}
}
ここで、メソッド A() を可能な限り 1000 倍速く実行したいメソッドがあるとします (非同期メソッドは個別のスレッドで実行できますが、他のすべてのコードは一度に 1 つずつ DOM にアクセスする必要があります)。非同期呼び出しは A() のコード実行に達し、B() と C() は一時停止されるため、A() を再度呼び出すことができます。
これを行うには2つの方法が考えられます。1 つは yield で、すべてのメソッドをイテレータに変更することで、実行を一時停止および再開できます。
struct DeferResult {
public object Result;
public bool Deferred;
}
IEnumerator<DeferResult> A() {
/*...A() code 1...*/
var dres = B();
if (dres.Deferred) yield dres;
/*...A() code 2...*/
}
IEnumerator<DeferResult> B() {
/*...B code 1...*/
var dres1 = C();
if (dres1.Deferred) yield dres1;
/*...B code 2...*/
var dres2 = C();
if (dres2.Deferred) yield dres2;
/*...B code 3...*/
}
IEnumerator<DeferResult> C() {
/*...C code 1...*/
if (rnd.NextDouble() < 0.3) { // unpredictable condition
startAsyncStuff();
yield return new DeferResult { Deferred = true; }
/*...C code 2 that uses async result above...*/
}
if (rnd.NextDouble() < 0.7) { // unpredictable condition
startOtherAsyncStuff();
yield return new DeferResult { Deferred = true; }
/*...C code 3 that might use any/both async results above...*/
}
yield return new DeferResult { Result = someResult(); }
}
void Main() {
var deferredMethods = new List<IEnumerator<DeferResult>>();
for (int i = 0; i < 1000; i++) {
var en = A().GetEnumerator();
if (en.MoveNext())
if (en.Current.Deferred)
deferredMethods.Add(en);
}
// then use events from the async methods so when any is done continue
// running it's enumerator to execute the code until the next async
// operation, or until finished
// once all 1000 iterations are complete call an AllDone() method.
}
このメソッドは、反復子からのオーバーヘッドがかなりあり、コード集約型ですが、すべてが 1 つのスレッドで実行されるため、DOM アクセスを同期する必要はありません。
別の方法は、スレッドを使用することです (1000 の同時スレッドは悪い考えなので、ある種のスレッド プーリングを実装します) が、これにはコストのかかる DOM アクセスの同期が必要です。
これらの条件下でコードの実行を延期するために使用できる他の方法はありますか? これを行うにはどのような方法が推奨されますか?