Task.Factory и связь между потоками - PullRequest
1 голос
/ 12 марта 2012

У меня есть один поток, который генерирует элементы графического интерфейса в wpf.Здесь есть холст для рисования объектов (прямоугольников и т. Д.)

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

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

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

Calc-поток запускает событие (DataReady), которое реализуется wpf-потоком:

void MyRegStringObject_DataReady()
{
  if (DebugMode)
    MyDrawingBoard.DrawRegElements();
}

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

В стеке есть некоторые вопросы с ответом, относящиеся к этой ошибке, но ни один из них не может помочь в моем случае.* Функция DrawRegElements () хочет очистить объект холста (среди прочего):

curCanvas.Children.Clear();

В этой позиции в коде выдается ошибка.Похоже, что функция MyRegStringObject_DataReady, запускаемая событием из calc-потока, также связана с calc-потоком.Но в классе определено, на чем основан поток wpf.

Как я могу решить эту проблему?У кого-нибудь есть идеи?Кстати: Calc-поток называется так:

CalcElements = Task.Factory.StartNew<bool>(MyRegStringObject.CalcRegElements);

Когда поток закончен, я определил:

CalcElements.ContinueWith((FinishCalcRegElements) =>
{
  MyDrawingBoard.DrawRegElements();
}, CancellationToken.None, TaskContinuationOptions.None, 
TaskScheduler.FromCurrentSynchronizationContext());

Никаких проблем с этим.Все работает идеально.Кажется, что функция, определенная в ContinueWith, связана с wpf-потоком.

1 Ответ

2 голосов
/ 12 марта 2012

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

Application.Current.Dispatcher.BeginInvoke((ThreadStart)delegate
   {            
       MyDrawingBoard.DrawRegElements();   
   });

Чтобы быть уверенным, что вы используете правильный Dispatcher, связанный с потоком основного пользовательского интерфейса, вы можете передать его в качестве параметра в код рабочего потока, поэтому просто передайте Dispatcher.Current из потока основного пользовательского интерфейса, в противном случае вызовите Dispatcher.CurrentDispatcher в рабочий поток инициализирует новый экземпляр, связанный с вызывающим рабочим потоком. Или просто используйте Application.Current.Dispatcher, который будет ссылаться на диспетчер потока основного пользовательского интерфейса.

PS:

Но в классе определено, что wpf-поток основан на

Это неверное предположение, сами классы не связаны ни с каким потоком. Конкретные экземпляры класса. Таким образом, вы можете создавать UI / не связанные с UI классы либо в пользовательском интерфейсе, либо в рабочем потоке.

Также важный момент, в каком потоке DataReady событие было запущено?

...