views:

62

answers:

1

I have many methods calling each other that each have to certain tasks, some of them asynchronous, that all operate on a DOM (so only one thread must access the DOM at any time).

For example:

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...*/
    }
}

Now let's say I have a method that wants to execute method A() 1000 times as fast as possible (the async methods can run in separate threads, however all other code must only access the DOM one at a time), so Ideally when the async calls are reached code execution for A(), B() and C() are paused, so A() can be called again.

There are 2 ways I can think of to do this. One is with yield, by changing all the methods to iterators I can pause and resume execution:

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.
}
  • This method has quite some overhead from the iterators, and is a bit more code intensive, however it all runs on one thread so I don't need to synchronize the DOM access.

  • Another way would be to use threads (1000 simultaneous threads are a bad idea, so i'd implement some kind of thread pooling), but this requires synchronizing DOM access which is costly.

Are there any other methods I can use to defer code execution under these conditions? What would be the recommended way to do this?

+1  A: 

As Karl has suggested, does this need to be multi-threaded? I may go for multi-threaded situation if

  1. DOM access are random but not frequent
  2. All other code in A, B, C is substantial in terms of time (as compared to DOM Access code)
  3. All other code in A, B, C can be executed in thread-safe way w/o doing any locking etc i.e. if they depend on some shared state then you have synchronize access to that as well as.

Now in such case, I would consider using a thread pool to launch A multiple times with synchronizing access to DOM. Cost of DOM synchronization can be reduced using thread-safe caching - of course that depends upon a kind of DOM access.

VinayC