асинхронный обратный вызов - PullRequest
1 голос
/ 11 октября 2011

Я новичок в асинхронном программировании. У меня есть C # dll с асинхронным методом, который вызывается, принимает указатель на функцию (делегат) и вызывает эту функцию обратного вызова после вычисления «результата».

public delegate void CreatedDelegate(Foo result);

public void CreateAsync(CreatedDelegate createdCallback)
    {
        Task t = Task.Factory.StartNew(() =>
                                       {
                                        Foo result = ...
                                        createdCallback(result);
                                       });
    }

Обратный вызов делегата типа «CreatedDelegate» является (в моем случае) указателем на метод C ++ / CLI, который работает с результатом.

void CreatedCallback(Foo^ result)
{
    // do something with result
}

Так что в большинстве случаев эта асинхронная концепция, кажется, работает достаточно хорошо, но иногда я сталкиваюсь с некоторыми ошибками. Как я могу добиться этого, если функция «CreateAsync» вызывается несколько раз с разными вычислительными усилиями, чтобы результирующие вызовы «CreatedCallback» просто происходили в том же порядке, в котором первоначально вызывался «CreateAsync»? Чтобы сделать его более понятным: первый вызов «CreateAsync» должен привести к первому вызову «CreatedCallback», даже если последующий вызов «CreateAsync» будет быстрее и фактически вызовет обратный вызов ранее.

Может быть, это можно сделать, разрешив только один активный новый поток в асинхронном "CreateAsync" одновременно?

Ответы [ 2 ]

1 голос
/ 11 октября 2011

Чтобы обработать обратные вызовы по порядку, вам необходимо реализовать некоторые очереди рабочих элементов. Самый простой способ - использовать тип BlockingCollection (см. документация MSDN ).

Вместо вызова обратного вызова, ваш метод CreateAsync добавит задачу (вместе с обратным вызовом) в очередь:

// Queue to keep tasks and their callbacks
private BlockingCollection<Tuple<Task<Foo>, CreatedDelegate>> 
  queue = new BlockingCollection<Tuple<Task<Foo>, CreatedDelegate>>()

public void CreateAsync(CreatedDelegate createdCallback) {
    Task<Foo> t = Task.Factory.StartNew(() =>  { 
      Foo result = ... 
      return result; });
    queue.Add(Tuple.Create(t, createdCallback));
    // ..
}

Это только добавит задачи и обратные вызовы в очередь - для фактического вызова обратного вызова вам понадобится другая задача, которая ожидает задачи в очереди (в том порядке, в котором они были добавлены) и вызывает обратный вызов:

Task.Factory.StartNew(() => { 
  while(true) { // while you keep calling 'CreateAsync'
    // Get next task (in order) and its callback
    Tuple<Task<Foo>, CreatedDelegate> op = queue.Take();
    // Wait for the result and give it to callback
    op.Item2(op.Item1.Result);
  }
}
0 голосов
/ 11 октября 2011

Если порядок важен, тогда лучше использовать потоки:

thread queue = empty
for each task
{
  if there are no free 'cpu'
    wait on first thread in queue
    remove thread from queue
    call delegate

  create thread
  add thread to queue
}

while queue has threads
  wait on first thread in queue
  remove thread from queue
  call delegate
...