У меня есть много методов, вызывающих друг друга, каждый из которых имеет определенные задачи, некоторые из них асинхронны, все они работают с 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, который является дорогостоящим.
Есть ли какие-либо другие методы, которые я могу использовать для отсрочки выполнения кода в этих условиях? Каким будет рекомендуемый способ сделать это?
Я думаю, что замок сделает это. И для опроса задач используйте сборку в параллельной библиотеке задач, если вы используете .Net 4.0 или иначе ThreadPoll. –
Согласен, но вы можете рассмотреть изменение дизайна. Зачем вам это многопоточно? Вы будете сериализовать доступ к DOM, чтобы только один поток получал доступ к нему за раз, и вы теряете преимущества потоковой передачи. –