推迟代码执行的最佳方法是什么?

发布于 2024-09-13 20:10:04 字数 2742 浏览 8 评论 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()。

我可以想到两种方法来做到这一点。一种是使用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 访问,这是昂贵的。

在这些情况下我可以使用其他方法来推迟代码执行吗?推荐的方法是什么?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

空宴 2024-09-20 20:10:06

正如 Karl 所建议的,这需要多线程吗? 我可能会选择多线程情况

  1. 如果DOM 访问是随机的但不频繁,
  2. A、B、C 中的所有其他代码在时间方面都很重要(与 DOM Access 代码相比)
  3. A、B、C 中的所有其他代码都可以以线程安全的方式执行,不进行任何锁定等,即如果它们依赖于某些共享状态,那么您也可以同步访问该状态。

现在在这种情况下,我会考虑使用线程池来多次启动 A 并同步对 DOM 的访问。使用线程安全缓存可以降低 DOM 同步的成本 - 当然这取决于某种 DOM 访问。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文