Два вопроса о шаблоне AsyncCallback и IAsyncResult - PullRequest
15 голосов
/ 23 февраля 2011

Два вопроса по шаблону обратного вызова с AsyncCallback и IAsyncResult.

Я изменил вопрос с примером кода:

using System;
using System.Collections.Generic;
using System.Text;

namespace TestAsync
{
    class Program
    {
        private static Wrapper test = new Wrapper();

        static void Main(string[] args)
        {
            test.BeginMethod("parameter 1", "parameter 2", Callback);
            Console.ReadKey();
        }

        private static void Callback(IAsyncResult ar)
        {
            string result = test.EndMethod(ar);
        }
    }

    public interface ITest
    {
        IAsyncResult BeginMethod(string s1, string s2, AsyncCallback cb, object state);
        string EndMethod(IAsyncResult result);
    }

    public class Wrapper
    {
        private ITest proxy = new Test();

        public void BeginMethod(string s1, string s2, AsyncCallback cb)
        {
            proxy.BeginMethod(s1, s2, cb, proxy);
        }

        public string EndMethod(IAsyncResult result)
        {
            return ((ITest)(result.AsyncState)).EndMethod(result);
        }
    }

    public class Test : ITest
    {
        private string WorkerFunction(string a, string b)
        {
            // "long running work"
            return a + "|" + b;
        }

        public IAsyncResult BeginMethod(string s1, string s2, AsyncCallback cb, object state)
        {
            Func<string, string, string> function = new Func<string, string, string>(WorkerFunction);
            IAsyncResult result = function.BeginInvoke(s1, s2, cb, state);
            return result;
        }

        public string EndMethod(IAsyncResult result)
        {
            return (string)(result.AsyncState);
        }
    }

    public delegate TResult Func<T1, T2, TResult>(T1 t1, T2 t2);
}

НАЧАТЬ РЕДАКТИРОВАТЬ
Я начинаю видеть, что являетсяпродолжается.Я перепутал асинхронный шаблон WCF и обычный асинхронный шаблон.В WCF используется прокси, а Begin- и EndMethod должны быть переданы прокси, а не делегату функции.В случае WCF литье работает, в обычном случае нет.WCF использует атрибут [OperationContract (AsyncPattern = true)], вероятно, для реализации несколько иного шаблона.END EDIT

Почему ошибка в строке return (string)(result.AsyncState);?
Точно такой же шаблон в рабочем коде в порядке.

Во-вторых, почему я не могу отлаживать код в BeginMethod класса Test?
Я могу только взломать WorkerFunction.

Ответы [ 2 ]

27 голосов
/ 23 февраля 2011

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

public class Test
{
    private int WorkerFunction(string a, string b)
    {
        //this is the guy that is supposed to do the long running work 
        Console.WriteLine(a);
        Console.WriteLine(b);
        return a.Length + b.Length;
    }

    private void MyCallBack(IAsyncResult ar)
    {
        Func<string, string, int> function = ar.AsyncState as Func<string, string, int>;
        int result = function.EndInvoke(ar);
        Console.WriteLine("Result is {0}", result);
    }
    public void CallMethod()
    {
        Func<string, string, int> function = new Func<string, string, int>(WorkerFunction);
        IAsyncResult result = function.BeginInvoke("param1", "param2", MyCallBack, function);
    }


}

class Program
{

    static void Main(string[] args)
    {
        Test test = new Test();
        test.CallMethod();
    }
}

. Как вы видите, функция обратного вызова (MyCallBack) получает объект IAsyncResult, переданный обратно ему.Именно этот объект IAsynchResult AyncState дает вам исходный объект, который вы передали в вызове метода BeginInvoke.В этом случае (и в качестве общей практики) вы передаете сам делегат как объект (который был переменной с именем «функция»).После того, как был вызван обратный вызов, я затем вернул исходный объект делегата, запросив ar.AsyncState, затем вызвал EndInvoke, чтобы вернуть результат.

Что касается точки останова, которая не была достигнута, я боюсьМне нужно больше информации об этом.Что именно ты имеешь ввиду?Где этот оператор Console.WriteLine?

НОВЫЙ ОТВЕТ. Хорошо, вот моя версия вашего кода.По сути, независимо от того, откуда вы вызываете EndInvoke, вам нужно вызывать его для фактического объекта делегата (в вашем случае переменная «function», которую вы создаете, передает ей фактический объект IAsyncResult).Код, который у вас есть, пытается замаскировать эту возможность, но я должен сказать, что есть менее сложные способы сделать это.Я буду более чем рад написать для вас какую-нибудь обертку, если хотите.Сейчас я просто возвращаю вам ваш код с моим небольшим дополнением, которое должно заставить его работать.Поскольку вы используете переменные уровня класса, следовательно, я вынужден использовать один из них сам.На данный момент это не совсем потокобезопасно.Но здесь идет

using System;
using System.Collections.Generic;
using System.Text;

namespace TestAsync
{
    class Program
    {
        private static Wrapper test = new Wrapper();

        static void Main(string[] args)
        {
            var objectState = new object();
            test.BeginMethod("parameter 1", "parameter 2", Callback, objectState);
            Console.ReadKey();
        }

        private static void Callback(IAsyncResult ar)
        {
            string result = test.EndMethod(ar);
            Console.WriteLine(result);
        }
    }

    public interface ITest
    {
        IAsyncResult BeginMethod(string s1, string s2, AsyncCallback cb, object state);
        string EndMethod(IAsyncResult result);
    }

    public class Wrapper
    {
        private ITest proxy = new Test();

        public void BeginMethod(string s1, string s2, AsyncCallback cb)
        {
            proxy.BeginMethod(s1, s2, cb, proxy);
        }

        public string EndMethod(IAsyncResult result)
        {
            return ((ITest)(result.AsyncState)).EndMethod(result);
        }
    }

    public class Test : ITest
    {
        Func<string, string, string> _delgateObject;
        private string WorkerFunction(string a, string b)
        {
            // "long running work"
            return a + "|" + b;
        }

        public IAsyncResult BeginMethod(string s1, string s2, AsyncCallback cb, object state)
        {
            Func<string, string, string> function = new Func<string, string, string>(WorkerFunction);
            this._delgateObject = function;
            IAsyncResult result = function.BeginInvoke(s1, s2, cb, state);
            return result;
        }

        public string EndMethod(IAsyncResult result)
        {
            var test = result.AsyncState;
            return this._delgateObject.EndInvoke(result);
        }
    }

    public delegate TResult Func<T1, T2, TResult>(T1 t1, T2 t2);
}
2 голосов
/ 24 февраля 2011

Эта статья помогла мне понять, что происходит. Wcf OperationContract реализует специальный шаблон Async, который синхронно вызывает [Operation] в отдельном потоке. Begin [Operation] и End [Operation] используются для создания паттерна, но на самом деле они не будут вызываться. Таким образом, этот шаблон с его сигнатурами и атрибутами, по-видимому, идентичен выполнению синхронного вызова на клиенте через, например, BackgroundWorker.

Значение AsyncPattern [of OperationContract attribute] можно установить только в true для метода с подписью, совместимой с BeginOperation, а в определяющем контракте также должен быть метод сопоставления с подписью, совместимой с EndOperation. Эти требования проверяются во время загрузки прокси. AsyncPattern связывает лежащий в основе синхронный метод с парой Begin / End и коррелирует синхронное выполнение с асинхронным. Вкратце, когда клиент вызывает метод формы BeginOperation с AsyncPattern, установленным в true, он сообщает WCF не пытаться напрямую вызывать метод с этим именем в службе. Вместо этого он будет использовать поток из пула потоков для синхронного вызова основного метода (идентифицируемого по имени действия). Синхронный вызов заблокирует поток из пула потоков, а не вызывающего клиента. Клиент будет заблокирован только на малейший момент, необходимый для отправки запроса вызова в пул потоков. Метод ответа синхронного вызова коррелирует с методом EndOperation.

...