2009-05-16 2 views
0

Я пытаюсь разработать класс, поддерживающий вызов метода asynchronus. Это то, что я придумал до сих пор, однако я не уверен, что это «правильный путь» для этого.Асинхронная реализация

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

Может ли кто-нибудь, кто знает асинхронный рисунок, дать мне некоторую обратную связь? Правильно ли я делаю это?

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

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading; 
using System.ComponentModel; 

namespace ConsoleApplication1 { 

    public delegate void WorkerDelegate(); 

    class Program { 

     static void Main(string[] args) { 

      String taskId = new Guid().ToString(); 

      AsyncTest test = new AsyncTest(); 
      test.DoSomethingLongAsyncCompleted += new AsyncCompletedEventHandler(test_DoSomethingLongAsyncCompleted); 
      test.DoSomethingLongProgressChanged += new ProgressChangedEventHandler(test_DoSomethingLongProgressChanged); 
      test.DoSomethingLongAsync(ItsOver, taskId); 

      // Cancel after 2 seconds 
      Thread.Sleep(2000); 
      test.DoSomethingLongCancelAsync(); 

      Console.ReadLine(); //Pause the console window 
     } 

     static void test_DoSomethingLongProgressChanged(object sender, ProgressChangedEventArgs e) { 
      Console.WriteLine("Percent complete: " + e.ProgressPercentage); 
     } 

     static void test_DoSomethingLongAsyncCompleted(object sender, AsyncCompletedEventArgs e) { 
      Console.WriteLine("Cancelled? " + e.Cancelled); 
      Console.WriteLine("Task ID: " + (String)e.UserState); 
     } 

     static void ItsOver(IAsyncResult r) { 
      Console.WriteLine("Task ID: " + (String)r.AsyncState); 
     } 
    } 

    class AsyncTest { 

     IAsyncResult _asyncResult = null; 
     Object _stateObj = null; 
     AsyncCallback _callBackDelegate; 

     public event ProgressChangedEventHandler DoSomethingLongProgressChanged; 
     public event AsyncCompletedEventHandler DoSomethingLongAsyncCompleted; 


     public IAsyncResult DoSomethingLongAsync(AsyncCallback userCallback, Object userState) { 

      if (_stateObj != null) 
       throw new InvalidOperationException("Method already started"); 

      WorkerDelegate worker = new WorkerDelegate(DoSomethingLong); 

      _callBackDelegate = userCallback; 
      _asyncResult = worker.BeginInvoke(null, userState); 

      return _asyncResult; 
     } 

     public void DoSomethingLongCancelAsync() { 
      _stateObj = null; 
     } 

     public void DoSomethingLong() { 

      // Set state object if method was called synchronously 
      if (_stateObj == null) 
       _stateObj = new Object(); 

      for (int i = 0; i < 10; i++) { 

       //If state object is null, break out of operation 
       if (_stateObj == null) break; 

       Thread.Sleep(1000); 
       Console.WriteLine("Elapsed 1sec"); 

       if (DoSomethingLongProgressChanged != null) { 
        // Percentage calculation for demo only :-) 
        DoSomethingLongProgressChanged(this, new ProgressChangedEventArgs(i+1 * 10, _stateObj)); 
       } 
      } 

      // Only execute if method was called async 
      if (_callBackDelegate != null) { 
       _callBackDelegate(_asyncResult); 

       DoSomethingLongAsyncCompleted(
        this, 
        new AsyncCompletedEventArgs(null, (_stateObj == null), _asyncResult.AsyncState) 
       ); 
      } 
     } 
    } 
} 

ответ

2

Есть два основных способа обработки асинхронной модели, проверить эту статью MSDN на Asynchronous Programming Model. Кажется, вы пытаетесь использовать технику IAsyncResult. Я использую это только для операций с низким уровнем System IO.

Для пользовательского интерфейса или API я склоняюсь к созданию модели событий, поскольку я считаю, что ее легче обрабатывать. Для вашего случая вы можете отправить событие на QueueUserWorkItem, отслеживать SynchronizationContext и использовать его при запуске завершенного события. (Если вы используете WPF, вы можете использовать DispatchObject).

Вот класс ContactLoader, который я использовал ранее.

public class ContactLoader 
{ 
    public List<Contact> Contacts { get; private set; } 
    private readonly IRepository<Contact> contactsRepository; 

    public ContactLoader(IRepository<Contact> contactsRepository) 
    { 
     this.contactsRepository = contactsRepository; 
    } 

    public event AsyncCompletedEventHandler Completed; 
    public void OnCompleted(AsyncCompletedEventArgs args) 
    { 
     if (Completed != null) 
      Completed(this, args); 
    } 

    public bool Cancel { get; set; } 

    private SynchronizationContext _loadContext; 
    public void LoadAsync(AsyncCompletedEventHandler completed) 
    { 
     Completed += completed; 
     LoadAsync(); 
    } 
    public void LoadAsync() 
    { 
     if (_loadContext != null) 
      throw new InvalidOperationException("This component can only handle 1 async request at a time"); 

     _loadContext = SynchronizationContext.Current; 

     ThreadPool.QueueUserWorkItem(new WaitCallback(_Load)); 
    } 

    public void Load() 
    { 
     _Load(null); 
    } 

    private void _Load(object state) 
    { 
     Exception asyncException = null; 
     try 
     { 
      Contacts = contactsRepository.GetAll(); 

      if (Cancel) 
      { 
       _Cancel(); 
       return; 
      } 
     } 
     catch (Exception ex) 
     { 
      asyncException = ex; 
     } 

     if (_loadContext != null) 
     { 
      AsyncCompletedEventArgs e = new AsyncCompletedEventArgs(asyncException, false, null); 
      _loadContext.Post(args => 
      { 
       OnCompleted(args as AsyncCompletedEventArgs); 
      }, e); 
      _loadContext = null; 
     } 
     else 
     { 
      if (asyncException != null) throw asyncException; 
     } 
    } 

    private void _Cancel() 
    { 
     if (_loadContext != null) 
     { 
      AsyncCompletedEventArgs e = new AsyncCompletedEventArgs(null, true, null); 
      _loadContext.Post(args => 
      { 
       OnCompleted(args as AsyncCompletedEventArgs); 
      }, e); 
      _loadContext = null; 
     } 
    } 
} 
+0

Спасибо за ваш ответ. Хотя, все статьи, которые я видел в MSDN, не обсуждают отдельные Invokations. Я проверю это. Еще раз спасибо. –

+0

Я добавил некоторое обновление кода с помощью примера EventModel. Посмотрите, поможет ли это. – bendewey

+0

Спасибо за кучи для образца кода. Я продолжаю читать, чтобы посмотреть, что я могу найти. Вместо этого я мог бы просто использовать класс BackgroundWorker. –

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