Как я могу использовать задачи в этом контексте?

#c# #.net #task #task-parallel-library

#c# #.net #задача #задача-параллельная-библиотека

Вопрос:

 private void cmbPlatform_SelectedIndexChanged(object sender, EventArgs e)
{
    string platform = cmbPlatform.Text;
    UpcomingGameFinder gameFinder = new UpcomingGameFinder();
    Task.Factory.StartNew<IEnumerable<Game>>(() => gameFinder.FindUpcomingGamesByPlatform(platform))
        .ContinueWith((i) => PlaceGameItemsInPanel(i.Result));        
}

private void PlaceGameItemsInPanel(IEnumerable<Game> games)
{
    int yPosition = 0;
    foreach (var game in games)
    {
        GameItem item = new GameItem(game);
        item.Location = new Point(0, yPosition);
        panelGameItemsHolder.Controls.Add(item);
        yPosition  = 125;
    }
}
  

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

В принципе, завершение FindUpcomingGames () занимает много времени, я хочу получить возвращаемую коллекцию из этого метода и создать N количество объектов GameItem (которые являются пользовательскими элементами управления) и разместить их на панели, которая у меня есть в моих Windows Forms.

Я получаю это сообщение об ошибке:

Недопустимая операция между потоками: элемент управления ‘panelGameItemsHolder’, к которому осуществляется доступ из потока, отличного от потока, в котором он был создан.

Есть предложения?

Комментарии:

1. В чем на данный момент здесь проблема? Это не компилируется или не дает вам правильного результата?

2. @Tejs: Позвольте мне отредактировать, добавив больше информации. 🙂

Ответ №1:

Метод PlaceGameItemsInPanel должен переключать пользовательский интерфейс для взаимодействия с элементами пользовательского интерфейса. Самый простой способ сделать это — заставить ваш ContinueWith выполняться в потоке пользовательского интерфейса. т.Е.

 private void cmbPlatform_SelectedIndexChanged(object sender, EventArgs e)
{
    var uiContext = TaskScheduler.FromCurrentSynchronizationContext();
    string platform = cmbPlatform.Text;
    UpcomingGameFinder gameFinder = new UpcomingGameFinder();
    Task.Factory.StartNew<IEnumerable<Game>>(() => gameFinder.FindUpcomingGamesByPlatform(platform))
        .ContinueWith((i) => PlaceGameItemsInPanel(i.Result), uiContext);        
}    
  

Это предполагает, что cmbPlatform_SelectedIndexChanged происходит в потоке пользовательского интерфейса (что и будет, если он обрабатывает событие из поля со списком, как, похоже, здесь). Таким образом, вы можете получить ссылку на текущий SynchronizationContext, а затем использовать это в ContinueWith, чтобы убедиться, что выполняется с правильным SynchronizationContext.

Вы можете прочитать больше об этом на http://blogs.msdn.com/b/pfxteam/archive/2009/09/22/9898090.aspx

Комментарии:

1. Но как мне это сделать? Помните, что я новичок в задачах и потоковой обработке в целом. 🙂

Ответ №2:

Вы бы выполняли свою работу в .ContinueWith делегате … после того, как вы создадите свой объект, добавьте его в некоторую коллекцию (в идеале, в коллекцию observable), которая автоматически обновит пользовательский интерфейс. конечно, если вы не используете связанную наблюдаемую коллекцию, вам придется самостоятельно обновлять элементы управления пользовательского интерфейса (просто будьте осторожны с межпоточным взаимодействием)

Ответ №3:

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