Как решить эти проблемы с помощью асинхронного обратного вызова? - PullRequest
1 голос
/ 03 декабря 2009

Мне нужно запустить 5 алгоритмов параллельно, каждый из которых берет изображение в качестве входного и выдает изображение в качестве выходного. После того, как все это будет сделано, мне нужно отобразить 5 выходных изображений. Я использую асинхронный обратный вызов с использованием делегатов для этой задачи.

Итак, я создал 5 делегатов для этих 5 алгоритмов и назвал их как algo1Delegate.BeginInvoke ().

Алгоритмы работают нормально и выдают результат тоже. У меня две проблемы с отображением этих изображений.

Для отображения изображений я создал класс ImageViewer (форма windows с элементом picturebox).

//ImageViewer constructor
ImageViewer(Image img, String Title)
{
    this.pictureBox1.Image = img;
    this.Text = Title;
}

Я показываю такие изображения:

void showImage(Image image, String title)
{
    ImageViewer imageviewer = new ImageViewer(image, title);
    imageviewer.Show();
}

Так как мне нужно отобразить изображение после алгоритма. Я передаю new AsyncCallback(showImage) делегат для каждого из этих BeginInvoke () в качестве 3-го параметра


private void showImage(IAsyncResult iasycResult)
{
    MessageBox.Show("white" + Thread.CurrentThread.ManagedThreadId);


    // Retrieve the `caller` delegate.
    AsyncResult asycResult = (AsyncResult)iasycResult;
    caller = (Algo1Delegate)asycResult.AsyncDelegate;//### PROBLEM!!!

    // Retrieve the  string Title that is passed in algodelegate.BeginInvoke().
    string title = (string)iasycResult.AsyncState;
    Image outputImage = caller.EndInvoke(iasycResult);

    showImage(outputImage, title);

}
  1. Я думаю, вы можете увидеть проблему в приведенной выше функции обратного вызова. он работает только для Algo1 для других 4-х журналов, его необходимо преобразовать в Algo2Delegate, Algo3Delegate и т. д., поскольку asycResult.AsyncDelegate имеет тип object. Как я могу решить эту проблему? Как я могу заставить это работать и для других?

  2. Окно imageViewer перестает отвечать на запросы. Я не понимаю почему? ImageViewer объект инициализируется и отображается в том же потоке для каждого из этих алгоритмов. Почему он перестает отвечать.

  3. Есть ли другие альтернативные решения?

PS: я не могу объявить один тип делегата для всех алгоритмов, поскольку во входных параметрах есть некоторые различия.

EDIT:

Ну, у меня достаточно информации для первого и третьего вопросов. Я использовал отдельные обратные вызовы для каждого из этих алгоритмов. Моя вторая проблема до сих пор не решена. Я изменил конструктор ImageViewer (), чтобы проверить, выполняются ли они в двух разных потоках:

    public ImageViewer(Image img, String title)
    {
        InitializeComponent();
        if (pictureBox1.InvokeRequired) MessageBox.Show("You must Invoke()");
        else MessageBox.Show("No need of Invoke()");

        this.pictureBox1.Image = img;
        this.Text = title + " : Image Viewer";
    }

в каждом случае написано No need of Invoke(). Я не понимаю, в чем проблема. Может ли кто-нибудь, пожалуйста, обратиться к этому тоже? Я также не получаю никаких исключений. Просто окно перестает отвечать на запросы. Я проверил, вызывают ли алгоритмы какие-либо проблемы. Но нет, они не.

Ответы [ 5 ]

1 голос
/ 03 декабря 2009

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

  AsyncResult result = (AsyncResult)iresult;
  if (result.AsyncDelegate is AsyncDelegate1) {
    (result.AsyncDelegate as AsyncDelegate1).EndInvoke(iresult);
  }
  else if (result.AsyncDelegate is AsyncDelegate2) {
    (result.AsyncDelegate as AsyncDelegate2).EndInvoke(iresult);
  }
  //etc...
  ComputationResult answer = result.AsyncState as ComputationResult;

Тьфу. Вы действительно должны иметь индивидуальный метод обратного вызова для каждого типа делегата. Универсальный метод не может помочь здесь, ограничение не может быть типом делегата. Лямбда в вызове метода BeginInvoke выглядит не намного лучше:

  var task1 = new AsyncDelegate1(Compute1);
  var result1 = new ComputationResult("task1");
  task1.BeginInvoke(42, result1, 
    new AsyncCallback((ia) => {
      AsyncResult result = ia as AsyncResult;
      (result.AsyncDelegate as AsyncDelegate1).EndInvoke(ia);
      CommonCallback(result.AsyncState as ComputationResult);
    }), 
    result1);

Нах. Я бы решил это, используя только один тип делегата. Тип WaitCallback подходит, хотя и с неправильным именем, вы должны написать маленькие вспомогательные классы, которые хранят аргументы для цели делегата, чтобы вы могли передать его через аргумент WaitCallback.state.


Ваша вторая проблема вызвана тем, что вы создаете экземпляр ImageViewer в методе обратного вызова. Обратный вызов выполняется в потоке потоков, а не в потоке пользовательского интерфейса. InvokeRequired возвращает значение false, поскольку элемент управления PictureBox был создан в потоке потоков. Однако этот поток потоков не подходит для отображения компонентов пользовательского интерфейса, он не обрабатывает цикл сообщений. И имеет неправильное состояние квартиры. И это заканчивается слишком рано.

InvokeRequired вернет правильное значение (true), когда вы используете элемент управления, созданный в потоке пользовательского интерфейса. Ваша основная форма запуска, например. Или Application.OpenForms [0]. Однако нет смысла использовать InvokeRequired, вы точно знаете, что обратный вызов выполняется в неправильном потоке. Просто используйте BeginInvoke напрямую. Вызванный метод должен создать экземпляр ImageViewer.


Вы успешно изобретаете класс BackgroundWorker. Это именно то, что вы пытаетесь сделать. Но заботится о мельчайших деталях получения события RunWorkerCompleted в правильном потоке. Вы должны рассмотреть это.

1 голос
/ 03 декабря 2009

Вы должны заменить делегатов последовательной иерархией с необходимыми вам общими методами.

AsyncCallbackClass caller = (AlgoDelegate)asycResult.AsyncState;

Image img = caller.DoCallBack(iAsyncResult);

тогда у вас есть иерархия с:

class AsyncCallback1 : AsyncCallbackClass
{
   Image DoCallBack(IAsyncResult result)
   {
      // Call specific callback with specific parameters
   }
}

class AsyncCallback2 : AsyncCallbackClass
{
   Image DoCallBack(IAsyncResult result)
   {
      // Call specific callback with specific parameters
   }
}

По сути, вы будете создавать обратные вызовы в виде иерархии классов, чтобы «сигнатура» основного метода была одинаковой (метод, который принимает IAsyncResult) и возвращает изображение, но так, как каждый «делегат» ( который теперь является полным классом) реализует вызов уникален для каждой реализации.

Взгляните на Замените делегат наследованием .

Редактировать: Со страницы msdn .

true, если дескриптор элемента управления был созданный в другом потоке, чем вызывающий поток (указывающий, что вы должны сделать вызовы к контролю через метод вызова); в противном случае - ложь.

Я предполагаю, что вы создаете ImageBox в ImageViewer, а ImageViewer создается в обратном вызове, поэтому, по определению, ImageBox был создан тем же потоком и поэтому не должен вызываться.

0 голосов
/ 03 декабря 2009

@ 1. У вас есть пять делегатов, но вы определили общий метод обратного вызова для каждого. Таким образом, у вас будет проблема с выяснением того, что делегат фактически выполнил. Один из способов - использовать разные методы обратного вызова для каждого делегата.

@ 2 Не следует обновлять пользовательский интерфейс из потока, отличного от того, в котором он был создан. Если это правда, мы используем Control.Invoke, чтобы убедиться, что вызов перенаправляется в поток пользовательского интерфейса.

    MethodInvoker updateImageViewer = delegate
    {
        ImageViewer imageviewer = new ImageViewer(image, title);
        imageviewer.Show();
    };

    if (this.pictureBox1.InvokeRequired)
        this.pictureBox1.Invoke(updateImageViewer);
    else
        updateImageViewer();
0 голосов
/ 03 декабря 2009

Несколько месяцев назад я делал нечто подобное, я использовал ThreadPool:

Он управляет потоками для вас и довольно прост в использовании для задач, не требующих сложной многопоточности.

0 голосов
/ 03 декабря 2009

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

private void run(Action<Image,Image> delegate, Image inputImage)
{
   delegate.BeginInvoke(inputImage, // all the callback stuff here );
}

Но затем вызовите ваш метод запуска с лямбдами:

run(image => algo1(image, otherVar, otherVar2));
run(image => algo2(image, otherVar, otherVar2, otherVar3, otherVar4));

и т. Д.

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