2011-02-23 62 views
15

有关AsyncCallback和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); 
} 

BEGIN编辑
我开始看是怎么回事。 我已经混合了一个WCF异步模式和一个正常的异步模式。 在WCF中,一个使用代理,Begin-和EndMethod必须通过代理而不是函数委托。在WCF的情况下,铸件起作用,在正常情况下不起作用。 WCF使用[OperationContract(AsyncPattern = true)]属性可能会强制执行某种不同的模式。 END编辑

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

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

回答

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

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

NEW RESPONSE 好的,这里是我的代码版本。基本上无论你从哪里调用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); 
} 
+0

您可以使用'object @ object'状态参数填充委托函数 - 您知道这是否是必需的吗? – Gerard 2011-02-23 13:20:48

+0

而不是'IAsyncResult result = function.BeginInvoke(...'你也可以做'function.BeginInvoke(...'? – Gerard 2011-02-23 13:35:07

+0

对不起,CallBack应该是WorkerFunction,我现在就改变它。用@object状态参数填充委托函数,但是你可以看到这样做可以让你访问回调函数体内的委托,你需要从回调函数中调用委托的EndInvoke,所以你需要委托函数引用那里。是的,你可以简单地做function.BeginInvoke。我保持使用与你使用的语法相同的语法。正如你所看到的,我不会在BeginInvoke – Nikhil 2011-02-23 14:13:15

2

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

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