Сделайте несколько асинхронных вызовов и сделайте что-нибудь, когда они завершатся - PullRequest
3 голосов
/ 24 июня 2009

Я столкнулся с этой проблемой на разных языках программирования, и мне было просто интересно, как лучше ее решить.

У меня есть три вызова методов, которые запускаются асинхронно. У каждого есть обратный звонок. Я хочу сделать что-то только после завершения всех трех обратных вызовов.

Как лучше всего это закодировать? Я обычно получаю все эти открытые флаги bool, и когда вы добавляете больше вызовов, код становится все более замысловатым.

Ответы [ 5 ]

3 голосов
/ 24 июня 2009

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

1 голос
/ 24 июня 2009

Если вы действительно хотите дождаться окончания all :

  1. Создать изменчивый счетчик
  2. Синхронизировать доступ к счетчику
  3. Увеличение счетчика при старте
  4. Снижено при обратном вызове
  5. Подождите, пока счетчик достигнет 0
0 голосов
/ 22 сентября 2010

Для тех, кто использует JavaScript, рассмотрите возможность использования шаблона, обсуждаемого в этом вопросе Stackoverflow: javascript: выполнить набор асинхронных методов с одним обратным вызовом

0 голосов
/ 24 июня 2009

Фьючерсы очень просты в использовании. Фьючерсы выглядят как обычные функции, за исключением того, что они выполняют асинхронное выполнение.

Занятия:

</p>

<pre><code>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;
    }
}

Здесь я использую фьючерсы для симуляции тупика:


        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());
            }
        }

0 голосов
/ 24 июня 2009

Использовать семафор .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...