关于AsyncCallback和IAsyncResult模式的两个问题

时间:2011-02-23 09:34:30

标签: c# begininvoke iasyncresult

有关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)]属性可能强制执行稍微不同的模式。 结束编辑

为什么行return (string)(result.AsyncState);上出现错误? 生产代码中的模式完全相同。

其次,为什么我不能在类Test的BeginMethod中调试代码?
我只能打破WorkerFunction。

2 个答案:

答案 0 :(得分:27)

让我给你这个示例代码,让事情变得清晰。请创建一个新的控制台应用程序并使用此

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方法调用中传递的原始对象。在这种情况下(作为一般做法),您将委托本身作为对象传递(这是一个名为“function”的变量)。 一个回调被调用,然后通过查询ar.AsyncState得到原始委托对象,然后我在其上调用EndInvoke以获取结果。

至于没有被击中的断点,恐怕我需要更多的信息。你到底什么意思?这个Console.WriteLine语句在哪里?

新回应 好的,这是我的代码版本。基本上无论你从哪里调用EndInvoke,你都需要在实际的委托对象上调用它(在你的情况下是你实例化的“函数”变量,传递实际的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);
}

答案 1 :(得分:2)

This article帮助我理解发生了什么。 Wcf的OperationContract实现了一个特殊的异步模式,它在一个单独的线程上同步调用[Operation]。开始[操作]和结束[操作]用于创建模式但不会真正调用它们。因此,具有其签名和属性的该模式似乎与通过例如在客户端上进行同步调用相同。一个BackgroundWorker。

您只能在具有BeginOperation兼容签名的方法上将AsyncPattern [of OperationContract attribute]设置为true,并且定义合约还必须具有与EndOperation兼容签名的匹配方法。在代理加载时验证这些要求。 AsyncPattern所做的是将底层同步方法与Begin / End对绑定,并将同步执行与异步执行相关联。简而言之,当客户端调用BeginOperation形式的方法并将AsyncPattern设置为true时,它会告诉WCF不要尝试直接通过服务上的该名称调用方法。相反,它将使用线程池中的线程来同步调用底层方法(由Action名称标识)。同步调用将阻塞线程池中的线程,而不是调用客户端。只有在将调用请求分派给线程池所需的最轻微时刻才会阻止客户端。同步调用的reply方法与EndOperation方法相关联。