Каков наилучший способ отложить выполнение кода? - PullRequest
0 голосов
/ 10 августа 2010

У меня есть много методов, вызывающих друг друга, которые у каждого есть для определенных задач, некоторые из них асинхронные, которые все работают на DOM (поэтому только один поток должен обращаться к 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 раз быстрее, чем это возможно (асинхронные методы могут выполняться в отдельных потоках , однако весь другой код должен иметь доступ только к DOM за один раз), поэтому в идеале, когда достигнуты асинхронные вызовы, выполнение кода для A (), B () и C () приостанавливается, поэтому A () можно вызывать снова.

Есть два способа сделать это. Один из них - с помощью 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.
}
  • Этот метод требует некоторых дополнительных затрат от итераторов и требует немного больше кода, однако все это выполняется в одном потоке, поэтому мне не нужно синхронизировать доступ DOM.

  • Другим способом было бы использование потоков (1000 одновременных потоков - плохая идея, поэтому я бы реализовал некоторый пул потоков), но для этого требуется синхронизация доступа к DOM, что является дорогостоящим.

Существуют ли другие методы, которые я могу использовать для отсрочки выполнения кода в этих условиях? Каков был бы рекомендуемый способ сделать это?

1 Ответ

1 голос
/ 12 августа 2010

Как предположил Карл, нужно ли это быть многопоточным? Я могу пойти на многопоточную ситуацию, если

  1. Доступ к DOM является случайным, но не частым
  2. Весь другой код в A, B, C является существенным с точки зрения времени (по сравнению с кодом доступа DOM)
  3. Весь другой код в A, B, C может быть выполнен потокобезопасным способом без какой-либо блокировки и т. Д., Т. Е. Если они зависят от какого-то общего состояния, то у вас также есть доступ к синхронизации.

Теперь в таком случае я хотел бы рассмотреть возможность использования пула потоков для многократного запуска A с синхронизацией доступа к DOM. Стоимость синхронизации DOM может быть уменьшена с помощью потокового кэширования - конечно, это зависит от типа доступа к DOM.

...