2010-07-23 6 views
0

J'essaie d'envelopper ma tête autour de l'appel asynchrone des méthodes.Comment appeler une méthode de manière asynchrone? Dois-je créer une classe dérivée de IAsyncResult?

public IDictionary<string, int> DoSomething(string startsWith) 
{ 
    return new Dictionary<string, int>(); 
} 

public IAsyncResult BeginDoSomething(string startsWith, AsyncCallback callback, 
            object state) 
{ 
    return new Func<string, IDictionary<string, int>>(DoSomething) 
      .BeginInvoke(startsWith, callback, state); 
} 

public IDictionary<string, int> EndDoSomething(IAsyncResult result) 
{ 
    // How to return the IDictionary?! 
} 

Mon problème est que je n'ai aucune idée de comment obtenir l'IDictionary dans la méthode EndDoSomething. J'ai googlé et j'ai vu que certaines personnes utilisaient l'état et le callback, alors que d'autres créaient leur propre classe dérivée de IAsyncResult, la renvoyaient de Begin et l'invoquaient dans End.

Est-ce vraiment la bonne façon de le faire? Ou quelle serait la bonne façon?

Répondre

1

Un peu désagréable à la recherche, mais cela ne devrait le faire (pas explicitement testé):

public IDictionary<string, int> DoSomething(string startsWith) 
{ 
    return new Dictionary<string, int>(); 
} 

public IAsyncResult BeginDoSomething(string startsWith, AsyncCallback callback, 
            object state) 
{ 
    var doSomething = new Func<string, IDictionary<string, int>>(DoSomething); 

    return doSomething.BeginInvoke(startsWith, callback, new object[] { doSomething, state }); 
} 

public IDictionary<string, int> EndDoSomething(IAsyncResult result) 
{ 
    var doSomething = (Func<string, IDictionary<string, int>>)((object[])result.AsyncState)[0]; 

    return doSomething.EndInvoke(result); 
} 
0

vous devez créer un délégué et appeler délégué de façon asynchrone.

public delegate IDictionary<String, Int> MyDelegate(string s); 
MyDelegate delObj = new MyDelegate(DoSomething); 
string str = "testString"; 
delObj.BeginInvoke(str, new AsyncCallback(CallbackMethod), null); 

et

private void CallbackMethod(IAsyncResult iresult) 
{ 
//logic 
AsyncResult iResult = iresult as AsyncResult; 
MyDelegate del = iResult.AsyncDelegate as MyDelegate; 
IDictionary<string,int> result = del.EndInvoke(iresult); 
} 

http://www.codeproject.com/KB/cs/AsyncMethodInvocation.aspx

pas testé, vous pouvez l'essayer et me faire savoir Merci

0

Il y a 2 fils qui seraient en cours d'exécution: (1) MainThread et (2) le thread de travail qui exécute DoSomething. Thread (2) va appeler votre callback, donc il ne peut rien retourner. Au lieu de cela, le rappel devrait récupérer le résultat et le placer dans un champ.

Le thread principal peut alors vérifier la valeur de ce champ.

Voici un exemple:

using System; 
using System.Collections.Generic; 
using System.Threading; 

namespace Commons.CLI.Sandbox 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      var myMain = new Program(); 
      myMain.Run(); 

      Console.WriteLine("Result: " + myMain.Output["start"] + " on thread: " + Thread.CurrentThread.ManagedThreadId); 
      Console.ReadKey(); 
     } 

     public IDictionary<string, int> Output { get; set; } 
     private object _outputLock = new object(); 

     private DoSomethingResultRetriever _methodToCall; 

     private ManualResetEvent _waiter; 

     public void Run() 
     { 
      _waiter = new ManualResetEvent(false); 

      _methodToCall = DoSomething; 
      var asyncResult = BeginDoSomething("start"); 

      // We need to wait for the other thread to run 
      Console.WriteLine(String.Format("Thread {0} is about to wait.", Thread.CurrentThread.ManagedThreadId)); 

      // Do other things ... 

      if (asyncResult.IsCompleted) return; 

      _waiter.WaitOne(); 
     } 

     private IAsyncResult BeginDoSomething(string startsWith) 
     { 
      return _methodToCall.BeginInvoke(startsWith, EndDoSomething, null); 
     } 

     private void EndDoSomething(IAsyncResult ar) 
     { 
      lock (_outputLock) 
      { 
       Output = _methodToCall.EndInvoke(ar); 
      } 

      _waiter.Set(); 
     } 

     private delegate IDictionary<string, int> DoSomethingResultRetriever(string startsWith); 

     private IDictionary<string, int> DoSomething(string startsWith) 
     { 
      Console.WriteLine("DoSomething on thread: " + Thread.CurrentThread.ManagedThreadId); 
      return new Dictionary<string, int>() { { startsWith, 10 } }; 
     } 
    } 
} 
0

Je pense que le plus simple est d'utiliser la classe Task<TResult> (ajoutée dans .NET 4), qui hérite de IAsyncResult:

public IDictionary<string, int> DoSomething(string startsWith) 
{ 
    return new Dictionary<string, int>(); 
} 

public IAsyncResult BeginDoSomething(string startsWith, AsyncCallback callback, 
            object state) 
{ 
    return Task.Factory.StartNew(_ => { return DoSomething(startsWith); }, state); 
} 

public IDictionary<string, int> EndDoSomething(IAsyncResult result) 
{ 
    var task = (Task<IDictionary<string, int>>)result; 
    return task.Result; 
} 

Non seulement Task<TResult> mettre en œuvre IAsyncResult, mais il gère également le marshaling des erreurs à l'appelant; ils sont relevés automatiquement en accédant au Task<TResult>.Result.

+0

Oui, cela semble mieux que BeginInvoke avec un délégué. –

Questions connexes