2010-02-12 6 views
11

У нас есть этот общий сценарий, в котором у нас есть метод, который выполняет какое-то действие асинхронно и вызывает событие, когда это будет сделано.Есть ли общий способ синхронизации асинхронного метода?

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

ManualResetEvent reset = new ManualResetEvent(false); 
someobject.AsyncActionDone += (sender, args) => reset.Set(); 
someobject.PerformAsyncAction(); 
reset.WaitOne(); 

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

Предпочтительно решение, которое не требует отражения

Там, кажется, некоторое замешательство, это пример того, что SomeObject своего класса, как:

public class SomeClass 
{ 
    private ExternalServer someServerOverTheNetwork = new ExternalServer(); 

    public event EventHandler AsyncActionDone; 
    public Data SomeData { get; set; } 
    public void PerformAsyncAction() 
    { 
     someServerOverTheNetwork.GetSomeData(OnDataRetrived); 
    } 

    public Data OnDataRetrived(Data someData) 
    { 
     AsyncActionDone(this, new DataEventArgs(someData)); 
    } 
} 
+0

Вы являетесь разработчиком какого-либо объекта или это тип библиотеки, который нельзя изменить? –

+0

Мы, однако, таких объектов много. Большинство действий связаны с отправкой запроса на сервер и ожиданием возврата результата, следовательно, по умолчанию они асинхронны. – Davy8

+0

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

ответ

4

Я бы подумал об использовании Asynchronous Design Pattern в объектах, выполняющих асинхронную операцию.

public object Operation(object arg) 
{ 
    var ar = BeginOperation(arg, null, null); 

    return EndOperation(ar); 
} 

public IAsyncResult BeginOperation(object arg, AsyncCallback asyncCallback, object state) 
{ 
    AsyncResult asyncResult = new AsyncResult(asyncCallback, state); 

    // Lauch the asynchronous operation 

    return asyncResult; 
} 

private void LaunchOperation(AsyncResult asyncResult) 
{ 
    // Do something asynchronously and call OnOperationFinished when finished 
} 

private void OnOperationFinished(AsyncResult asyncResult, object result) 
{ 
    asyncResult.Complete(result); 
} 


public object EndOperation(IAsyncResult asyncResult) 
{ 
    AsyncResult ar = (AsyncResult)asyncResult; 

    return ar.EndInvoke(); 
} 

С помощью этого шаблона вы обладаете гибкостью при одновременной асинхронной работе с вашим объектом.

Примечание: Вы можете легко найти реализацию общего класса AsyncResult в Интернете.

Edit:

Как вы хотите сохранить текущий проект, если все ваш объект может иметь только одну асинхронную операцию, то вы могли бы определить интерфейс IAsyncOperation и реализовать его во всем вашем объекте.

public interface IAsyncOperation 
{ 
    event EventHandler AsyncActionDone; 
    void PerformAsyncAction(); 
} 

Тогда вы могли бы:

public static CallSynchronously(IAsyncOperation asyncOperation) 
{ 
    ManualResetEvent reset = new ManualResetEvent(false); 
    asyncOperation.AsyncActionDone += (sender, args) => reset.Set(); 
    asyncOperation.PerformAsyncAction(); 
    reset.WaitOne(); 
} 

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

public void PerformAction() 
{ 
    ManualResetEvent reset = new ManualResetEvent(false); 
    this.AsyncActionDone += (sender, args) => reset.Set(); 
    this.PerformAsyncAction(); 
    reset.WaitOne(); 
} 
+0

Я снимаю решение, которое не включает модификацию класса 'someobject'. Текущий шаблон уже существует в десятках классов в нашей системе. – Davy8

+0

Отредактированный вопрос, с надеждой на лучшее объяснение – Davy8

+0

Отредактированный ответ –

3

Если я поймать свой дрейф, вы можете просто использовать делегат следующим образом создать делегат для вас функции вызова, позволяет называть его

public delegate string AsyncDelegate(); 

затем, создать прокси-FUNCT ион, как это:

public static void ExecuteSync(AsyncDelegate func) 
    { 
     ManualResetEvent reset = new ManualResetEvent(false); 
     int threadId; 
     func.BeginInvoke((a)=>reset.Set(), null); 
     reset.WaitOne(); 
    } 

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

Наслаждайтесь!

+0

Если я не ошибаюсь, предполагая, что у вас уже есть синхронная функция, не так ли? Когда я вызываю 'someobject.PerformAsyncAction();' он приостанавливает действие и сразу возвращается, я не вижу, передал ли я свою функцию вашему методу, который будет знать, когда действие действительно выполняется. – Davy8

+0

Когда действие завершено, он вызывает обратный вызов, который «устанавливает» ManualResetEvent. Основная функция не вернется, пока ManualResetEvent не будет установлен. – Foole

+0

Как узнать, когда действие закончено? Вся функция Action выполняет отправку сообщения, а затем немедленно возвращается. – Davy8

Смежные вопросы