Асинхронная реализация - PullRequest
0 голосов
/ 16 мая 2009

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

Я хочу, чтобы асинхронный метод выполнялся только один раз, он не должен поддерживать несколько выполнений, поэтому я не использовал класс 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)
                );
            }
        }
    }
}

1 Ответ

2 голосов
/ 16 мая 2009

Существует два основных способа обработки асинхронной модели, ознакомьтесь с этой статьей MSDN по модели асинхронного программирования . Кажется, вы пытаетесь использовать технику IAsyncResult. Я склонен использовать это только для операций ввода-вывода низкого уровня.

Для пользовательского интерфейса или 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;
        }
    }
}
...