【问题标题】:Make multiple asynchronous calls and do something when they are completed进行多次异步调用,并在它们完成时执行某些操作
【发布时间】:2009-06-24 15:10:45
【问题描述】:

我在多种编程语言中都遇到过这个问题,我只是想知道处理它的最佳方法是什么。

我有三个异步触发的方法调用。每个都有一个回调。我只想在所有三个回调都完成后才做某事。

编写此代码的最佳方法是什么?我通常会使用所有这些公共 bool 标志,当您添加更多调用时,代码会变得更加复杂。

【问题讨论】:

    标签: asynchronous coding-style


    【解决方案1】:

    来自 C#,我可能会使用 WaitHandle.WaitAll。您可以创建一个ManualResetEvent 对象数组(每个要完成的任务一个),然后将该数组传递给WaitAll。线程化的任务将分别获得一个 ManualResetEvent 对象,并在准备就绪时调用Set 方法。 WaitAll 将阻塞调用线程,直到所有任务完成。我将给出一个 C# 代码示例:

    private void SpawnWorkers()
    {
        ManualResetEvent[] resetEvents = new[] {
                new ManualResetEvent(false), 
                new ManualResetEvent(false)
            };
    
        // spawn the workers from a separate thread, so that
        // the WaitAll call does not block the main thread
        ThreadPool.QueueUserWorkItem((state) =>
        {
            ThreadPool.QueueUserWorkItem(Worker1, resetEvents[0]);
            ThreadPool.QueueUserWorkItem(Worker2, resetEvents[1]);
            WaitHandle.WaitAll(resetEvents);
            this.BeginInvoke(new Action(AllTasksAreDone));
    
        });
    }
    private void AllTasksAreDone()
    {
        // OK, all are done, act accordingly
    }
    
    private void Worker1(object state)
    {
        // do work, and then signal the waiting thread
        ((ManualResetEvent) state).Set();
    }
    
    private void Worker2(object state)
    {
        // do work, and then signal the waiting thread
        ((ManualResetEvent)state).Set();
    }
    

    请注意,AllTasksAreDone 方法将在用于生成工作线程的线程池线程上执行,而不是在主线程上执行...我假设许多其他语言都有类似的构造。

    【讨论】:

    • 调用 this.BeginInvoke 时我们处于什么上下文中?什么是“这个”?
    【解决方案2】:

    如果您真的只想等待全部完成:

    1. 创建易失性计数器
    2. 同步访问计数器
    3. 开始时增加计数器
    4. 触发回调时减少
    5. 等待计数器达到 0

    【讨论】:

      【解决方案3】:

      使用semaphore

      【讨论】:

        【解决方案4】:

        Future 非常易于使用。 Futures 看起来像普通函数,除了它们执行异步。

        课程:

        
        
        
        public struct FutureResult<T>
        {
            public T Value;
            public Exception Error;
        }
        public class Future<T>
        {
            public delegate R FutureDelegate<R>();
            public Future(FutureDelegate<T> del)
            {
                _del = del;
                _result = del.BeginInvoke(null, null);
            }
            private FutureDelegate<T> _del;
            private IAsyncResult _result;
            private T _persistedValue;
            private bool _hasValue = false;
            private T Value
            {
                get
                {
                    if (!_hasValue)
                    {
                        if (!_result.IsCompleted)
                            _result.AsyncWaitHandle.WaitOne();
                        _persistedValue = _del.EndInvoke(_result);
                        _hasValue = true;
                    }
                    return _persistedValue;
                }
            }
            public static implicit operator T(Future<T> f)
            {
                return f.Value;
            }
        }
        

        这里我使用futures来模拟死锁:

        
                void SimulateDeadlock()
                {
                    Future> deadlockFuture1 = new Future>(() =>
                    {
                        try
                        {
                            new SystemData(ConfigurationManager.ConnectionStrings["DbConnectionString"].ConnectionString)
                                .SimulateDeadlock1(new DateTime(2000, 1, 1, 0, 0, 2));
                            return new FutureResult { Value = true };
                        }
                        catch (Exception ex)
                        {
                            return new FutureResult { Value = false, Error = ex };
                        }
                    });
                    Future> deadlockFuture2 = new Future>(() =>
                    {
                        try
                        {
                            new SystemData(ConfigurationManager.ConnectionStrings["DbConnectionString"].ConnectionString)
                                .SimulateDeadlock2(new DateTime(2000, 1, 1, 0, 0, 2));
                            return new FutureResult { Value = true };
                        }
                        catch (Exception ex)
                        {
                            return new FutureResult { Value = false, Error = ex };
                        }
                    });
                    FutureResult result1 = deadlockFuture1;
                    FutureResult result2 = deadlockFuture2;
                    if (result1.Error != null)
                    {
                        if (result1.Error is SqlException && ((SqlException)result1.Error).Number == 1205)
                            Console.WriteLine("Deadlock!");
                        else
                            Console.WriteLine(result1.Error.ToString());
                    }
                    else if (result2.Error != null)
                    {
                        if (result2.Error is SqlException && ((SqlException)result2.Error).Number == 1205)
                            Console.WriteLine("Deadlock!");
                        else
                            Console.WriteLine(result2.Error.ToString());
                    }
                }
        
        

        【讨论】:

        • 此示例中缺少某些类
        【解决方案5】:

        对于那些使用 JavaScript 的人,请考虑使用在 Stackoverflow 问题中讨论的模式: javascript: execute a bunch of asynchronous method with one callback

        【讨论】:

          猜你喜欢
          • 2013-10-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-07-15
          • 2014-09-20
          相关资源
          最近更新 更多