AsyncCallback和IAsyncResult模式的两个问题

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);
}

BEGIN EDIT
我开始明白发生了什么。 我混淆了WCF异步模式和普通异步模式。 在WCF中,需要使用代理并且Begin-和EndMethod必须传递代理而不是函数委托。 在WCF情况下强制转换有效,在普通情况下不起作用。 WCF使用[OperationContract(AsyncPattern = true)]属性可能是为了强制执行略有不同的模式。 END EDIT

为什么在return (string)(result.AsyncState);行会出错?
在生产代码中完全相同的模式是没问题的。

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

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对象的AsyncState属性给了你在BeginInvoke方法调用中所传递的原始对象。在本例中(以及一般情况下),你需要将委托本身作为对象传递进去(即变量"function")。

一旦回调被调用,我通过查询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);
}

你可以直接使用 function.BeginInvoke(...,而不是写成 IAsyncResult result = function.BeginInvoke(... 吗? - Gerard
我认为它的写作风格比预期的要稍微复杂一些。我能看出你的问题所在。给我几分钟,我会为你解决这个问题。 - Nikhil
好的,我改变了我的问题,因为我意识到我使用了一个额外的包装对象和接口。我正在调查一个WCF异步模式,当它被用作普通异步模式时会出现执行错误。 - Gerard
字符串string(result.AsyncState)会报错,因为result.AsyncStatus中的对象实际上不是WorkerFunction调用的结果,而是您在private ITest proxy = new Test()行中创建的代理对象。实际上您混淆了几件事情。让我看看是否能帮助您解决这个问题。 - Nikhil
@Nikhil 我已经修复了所有错误。请再次查看代码。 - NoWar
显示剩余2条评论

2

这篇文章帮助我理解了其中的原理。WCF的OperationContract实现了一种特殊的异步模式,它会在单独的线程上同步调用[Operation]。使用Begin[Operation]和End[Operation]创建此模式,但实际上不会真正调用它们。因此,这个模式及其签名和属性似乎与通过后台工作者在客户端上进行同步调用完全相同。

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接