2010-08-10 3 views
0

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

Есть два способа, которые я могу придумать для этого. Один с выходом, изменяя все методы для итераторов я могу приостановить и возобновить выполнение:

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, который является дорогостоящим.

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

+0

Я думаю, что замок сделает это. И для опроса задач используйте сборку в параллельной библиотеке задач, если вы используете .Net 4.0 или иначе ThreadPoll. –

+0

Согласен, но вы можете рассмотреть изменение дизайна. Зачем вам это многопоточно? Вы будете сериализовать доступ к DOM, чтобы только один поток получал доступ к нему за раз, и вы теряете преимущества потоковой передачи. –

ответ

1

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

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

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

Смежные вопросы