16

关于 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。

4

2 回答 2

28

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

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,您都需要在实际委托对象上调用它(在您的情况下,您实例化的“函数”变量,将实际的 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);
}
于 2011-02-23T11:12:20.210 回答
2

这篇文章帮助我理解了发生了什么。WcfOperationContract实现了一种特殊的 Async 模式,它在单独的线程上同步调用 [Operation]。Begin[Operation] 和 End[Operation] 用于创建模式,但不会真正调用它们。因此,这种带有其签名和属性的模式似乎与通过例如 BackgroundWorker 在客户端上进行同步调用相同。

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

于 2011-02-23T22:33:31.330 回答