Последовательный вызов асинхронного метода - PullRequest
2 голосов
/ 11 октября 2010

У меня есть следующий (упрощенный) асинхронный метод:

void Transform<X,Y>(X x, Action<Y> resultCallback) {...}

, и я хочу преобразовать список X в список Y.

Проблема в том, чтонесмотря на то, что метод Transform является асинхронным, он должен вызываться последовательно (т.е. мне приходится ждать обратного вызова, прежде чем вызывать его со следующим значением).

Есть ли способ сделать это элегантно?(Я на .Net 4.0)

Я предполагаю, что может быть какой-то способ сделать это с передачей продолжения ...

ОБНОВЛЕНИЕ Я забыл указатьчто я не хочу блокировать вызывающий поток (GUI).

Ответы [ 2 ]

4 голосов
/ 11 октября 2010

Если вы заключите это в вспомогательный класс, вы можете заставить помощника «синхронизировать» ваши значения:

public class AsyncWrapper<X,Y>
{
    ManualResetEvent mre;
    private Y result; 

    public Y Transform(X x, YourClass yourClass)
    {
        mre = new ManualResetEvent(false);
        result = default(Y);

        yourClass.Transform<X,Y>(x, this.OnComplete);
        mre.WaitOne();
        return result;
    }

    void OnComplete(Y y)
    {
        result = y;
        mre.Set();
    }        
}

Вы можете использовать это как:

// instance your class with the Transform operation
YourClass yourClass = new YourClass();

AsyncWrapper<X,Y> wrapper = new AsyncWrapper<X,Y>();

foreach(X x in theXCollection)
{
     Y result = wrapper.Transform(x, yourClass);

     // Do something with result
}

Edit:

Поскольку вы говорите, что пытаетесь сделать это, чтобы все работало в фоновом потоке, вы можете использовать мой код выше и сделать:

// Start "throbber"
Task.Factory.StartNew () =>
{
    // instance your class with the Transform operation
    YourClass yourClass = new YourClass();

    AsyncWrapper<X,Y> wrapper = new AsyncWrapper<X,Y>();

    foreach(X x in theXCollection)
    {
         Y result = wrapper.Transform(x, yourClass);

         // Do something with result
    }
}).ContinueWith( t =>
{
    // Stop Throbber
}, TaskScheduler.FromCurrentSynchronizationContext());

Это запустит весь (теперь синхронный) процесс в фоновом потоке и отключит ваш «пульсатор» (из комментария) в потоке пользовательского интерфейса после его завершения.

Если вы управляете всем этим кодом, вы можете сделать процесс преобразования синхронным с самого начала и просто переместить его в фоновый поток, как описано выше, избегая необходимости в обертке.

1 голос
/ 12 октября 2010

Как я намекнул в своем вопросе, я задумался о решении, использующем прохождение продолжения.Следующие методы расширения позволяют мне использовать довольно «симпатичное»:

public static class Extensions
{
    //Using an asynchronous selector, calculate transform for 
    //  input list and callback with result when finished
    public static void AsyncSelect<TIn, TOut>(this List<TIn> list, 
              Action<TIn, Action<TOut>> selector, 
              Action<List<TOut>> callback)
    {
        var listOut = new List<TOut>();
        list.AsyncSelectImpl(listOut, selector, callback);
    }

    //internal implementation - hides the creation of the output list
    private static void AsyncSelectImpl<TIn, TOut>(this List<TIn> listIn, 
              List<TOut> listOut, 
              Action<TIn, Action<TOut>> selector, 
              Action<List<TOut>> callback)
    {
        if(listIn.Count == 0)
        {
            callback(listOut); //finished (also if initial list was empty)
        }
        else
        {
            //get first item from list, recurse with rest of list
            var first = listIn[0];
            var rest = listIn.Skip(1).ToList();
            selector(first, result => { 
                            listOut.Add(result); 
                            rest.AsyncSelectImpl(listOut, selector, callback); 
                    });
        }
    }
}

На вызывающей стороне это приводит к:

    (...)
    //(For a Transform which performs int -> string)
    Throbber.Start();
    inList.AsyncSelect<int,string>(Transform, WhenDone);
}
private void WhenDone(List<string> outList)
{
    Throbber.Stop();
    //do something with outList
}

Одно очевидное ограничение - переполнение стекамои цели, это не будет проблемой (я в десятках предметов, а не в тысячах).Любые другие вопиющие помехи в комментариях, пожалуйста!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...