Задача ContinueWith вызывает межпотоковое исключение, несмотря на контекст пользовательского интерфейса - PullRequest
4 голосов
/ 06 сентября 2011

У меня сложилось впечатление, что использование метода ContinueWith в Task с контекстом пользовательского интерфейса позволяет выполнять операции над элементами пользовательского интерфейса, не вызывая межпотокового исключения. Тем не менее, я все еще получаю исключение при выполнении следующего кода:

var context = TaskScheduler.FromCurrentSynchronizationContext();

Task<SomeResultClass>.Factory.StartNew(SomeWorkMethod).ContinueWith((t) =>
   {
      myListControl.Add(t.Result); // <-- this causes an exception
   }, context);

Есть идеи?

Ответы [ 2 ]

7 голосов
/ 06 сентября 2011

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

var context = TaskScheduler.FromCurrentSynchronizationContext();

Task<SomeResultClass>.Factory.StartNew(SomeWorkMethod).ContinueWith((t) =>
   {
      if (!myListControl.InvokeRequired)
         myListControl.Add(t.Result); // <-- this causes an exception
      else
         myListControl.Invoke((Action)(() => myListControl.Add(t.Result)));
   }, context);

(при условии, что это WinForms)

если вы хотите изменить контроль над add в метод и использовать InvokeRequired внутри метода для вызова себя внутри Invoke, если необходимо:

private void AddToListControl(MyItem item)
{
   if (myListControl.InvokeRequired) 
   {
      myListControl.Invoke((Action)(() => AddToListControl(item)));
      return;
   }

   myListControl.Add(item);
}

То, на что намекал Enigmativity, выглядит примерно так:

var result =
   Task<Action>.Factory.StartNew(SomeWorkMethod).ContinueWith((t) =>
      {
         return () => myListControl.Add(t.Result);
      });

result.Result();

Но ИМХО, это то же самое место, которое вы получили с самого начала, потому что вам нужно еще раз вызвать Result-Action в нужном потоке.

7 голосов
/ 06 сентября 2011

Существуют две разные причины исключений между потоками.

Наиболее распространенным является попытка изменить состояние элемента управления из потока, не являющегося пользовательским интерфейсом. И это не проблема, которую вы решаете.

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

Чтобы сделать эту работу, вам необходимо отделить работу от создания элемента управления. Попробуйте вернуть Func<Control> вместо Control и вызвать его в потоке пользовательского интерфейса перед его добавлением. Сохраняйте большую часть работы над потоком задач, но формируйте хорошее плотное замыкание, возвращая Func<>, который просто создает элемент управления.

...