Правилен ли этот метод преобразования асинхронного метода в синхронный?

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

Я придумал следующий общий метод для преобразования асинхронного вызова на основе событий в синхронный:

 Func<TArg1, TArg2, TArg3, TEventArgs> 
    CreateSynchronousMethodFromAsync<TArg1, TArg2, TArg3, TEventArgs>(
       Action<TArg1, TArg2, TArg3, EventHandler<TEventArgs>> asyncMethod) 
         where TEventArgs : AsyncCompletedEventArgs
    {
        Func<TArg1, TArg2, TArg3, TEventArgs> syncMethod = (arg1, arg2, arg3) =>
         {
             TEventArgs eventArgs = null;

             using (var waitHandle = new ManualResetEvent(false))
             {
                 asyncMethod(arg1, arg2, arg3, (sender, e) =>
                                          {
                                              eventArgs = e;
                                              waitHandle.Set();
                                          });

                 waitHandle.WaitOne();

                 return eventArgs;
             }
         };

        return syncMethod;
    }

Итак, если у меня есть этот асинхронный метод:

void ConnectAsync(string address, 
     string userName, 
     string password, 
     EventHandler<ConnectCompletedEventArgs> completionCallback)

Я могу преобразовать его в синхронный вызов следующим образом:

public void Connect(string address, string userName, string password)
{
    Func<string, string, string, ConnectCompletedEventArgs> connect = 
        CreateSynchronousMethodFromAsync<string, string, string, ConnectCompletedEventArgs>(ConnectAsync);

    var connectResult = connect(address, userName, password);

    if (connectResult.Error != null)
    {
        throw connectResult.Error;
    }
}

Меня беспокоит использование переменной eventArgs, которая фиксируется в закрытии. Он устанавливается в одном потоке и доступен из другого. Достаточно ли моего использования ManualResetEvent, чтобы гарантировать правильное чтение значения после того, как событие было сообщено, или мне нужно сделать что-то еще?

Ну, вы в этом, вы можете прокомментировать обработку исключений здесь. Мой план состоит в том, что метод Async будет оборачивать исключения, которые происходят ниже по стеку, в ConnectionException или что-то в этом роде, поэтому я думаю, что повторное создание исключения в этом случае правильно.


person Samuel Jack    schedule 19.06.2009    source источник


Ответы (1)


Основываясь на шаблоне ASync, обсуждаемом на странице, на которую вы ссылались, это выглядит как довольно хорошая попытка обернуть асинхронный вызов.

Что заставляет меня сомневаться, так это название метода 'BeginConnect'; некоторые классы .NET имеют пары 'BeginXxx'/'EndXxx' для обработки асинхронных вызовов, и они обычно указывают, что правильная операция требует, чтобы вызов 'EndXxx' вызывался из обработчика обратного вызова, что является чем-то, что ваша схема не подходит.

Если вызов, который вы оборачиваете, действительно соответствует шаблону, обсуждаемому на связанной странице, это должно сработать, если оборачиваемый вами вызов относится ко второму типу, вы не совсем там...

person jerryjvl    schedule 19.06.2009
comment
Мой код предназначен только для использования с методами, соответствующими шаблону асинхронного метода на базе событий — мой внутренний метод BeginConnect, реализацию которого я не показывал, заботится о заключении вызовов Begin/End. - person Samuel Jack; 19.06.2009
comment
Обратите внимание, что вместо «throw connectionResult.Error» вы можете захотеть обернуть это как внутреннее исключение в новое исключение, чтобы сохранить трассировку стека исходного исключения. - person jerryjvl; 19.06.2009
comment
@Sam: я бы рекомендовал назвать метод на основе соглашения в связанной статье, а затем, чтобы избежать путаницы ... вместо этого сделайте его «ConnectAsync». - person jerryjvl; 19.06.2009