BackgroundWorker e.Result приводит к тому, что вызывающий поток не может получить доступ к ошибке

Я прочитал много вопросов SO, связанных с BackgroundWorker и DispatcherTimer, и понимаю, что вы не можете получить доступ к компонентам пользовательского интерфейса ни в каком потоке, кроме основного.

Итак, у меня есть DispatcherTimer, который тикает каждые 1/2 секунды. Как и следовало ожидать, я могу обновить класс модели представления и любой элемент пользовательского интерфейса, который требует прямых манипуляций, и пользовательский интерфейс довольно отзывчивый. Однако у меня есть расчет, основанный на значении в пользовательском интерфейсе, выполнение которого занимает около 3 секунд.

Я попытался просто выполнить вычисление в потоке DispatcherTimer, и это заблокировало / заблокировало пользовательский интерфейс до его завершения. В настоящее время у меня есть проверка в DispatcherTimer, которая затем запускает поток BackgroundWorker, который запускается и выполняет расчет. Я использую e.Arguments, чтобы передать моему 3-секундному вычислительному процессу необходимые данные и e.Result для завершенных данных, которые я получаю.

Я проверил результат, ошибок нет. Однако, когда я возвращаю e.Result обратно в свой класс, e.Result не может правильно оцениваться. Когда я использую свойство class, я получаю сообщение «вызывающий поток не может получить доступ к ошибке».

...
timerBackgroundLoop = new DispatcherTimer(DispatcherPriority.Background);
timerBackgroundLoop.Interval = TimeSpan.FromMilliseconds(500);
timerBackgroundLoop.Tick += new EventHandler(Timer_Tick);
timerBackgroundLoop.Start();
...

private void Timer_Tick(object sender, EventArgs e)
{
    if (MyClass.NeedToRebuildBuildMap)
    {
        MyClass.NeedToRebuildBuildMap = false; //stop next timer tick from getting here
        threadDrawMap = new BackgroundWorker();
        threadDrawMap.WorkerReportsProgress = false;
        threadDrawMap.WorkerSupportsCancellation = false;
        threadDrawMap.DoWork += new DoWorkEventHandler(threadDrawMap_DoWork);
        threadDrawMap.RunWorkerCompleted += new RunWorkerCompletedEventHandler(threadDrawMap_Completed);
        threadDrawMap.RunWorkerAsync(myClass.MapData);
    }
    ...
}

private void threadDrawMap_DoWork(object sender, DoWorkEventArgs e)
{
    MyClass.MapData _mapData = (MyClass.MapData)e.Argument;                        
    e.Result  = BuildMap(_mapData);
}

private void threadDrawMap_Completed(object sender, RunWorkerCompletedEventArgs e)
{
    MyClass.MapGeo = (List<PathGeometry>)e.Result;
    DrawUIMap(MyClass.MapGeo); //draws the map on the UI into a grid
}

Когда я устанавливаю перерыв в «threadDrawMap_Completed» и оцениваю List of PathGeometry, я получаю эту ошибку: base {System.SystemException} = {"Вызывающий поток не может получить доступ к этому объекту, потому что он принадлежит другому потоку."}

На самом деле я не вижу ошибки, пока не использую метод DrawUIMap и не пытаюсь получить доступ к списку геометрии MyClass.MapGeo. На этом этапе я снова в потоке DispatcherTimer, который может получить доступ к пользовательскому интерфейсу.

Насколько я знаю, я все сделал правильно, насколько и где / когда я обращаюсь к компонентам пользовательского интерфейса. Хотя мне кажется, что я где-то сделал что-то ужасно не так.

** Изменить: это часть кода, который выполняет расчет.

public static List<PathGeometry> BuildMap(List<PathGeometry> _geoList)
{
    ...
    List<PathGeometry> retGeoList = new List<PathGeometry>();
    retGeoList.Add(geoPath1);        
    retGeoList.Add(geoPath2);
    ...
    return retGeoList;
}

person MikeyTT    schedule 09.10.2013    source источник
comment
где вы сбрасываете MyClass.NeedToRebuildBuildMap, я не уверен, но использование флага в timer для предотвращения запуска следующего BackgroundWoker может быть не лучшим вариантом? может быть шанс, что флаг будет сброшен, и прежде, чем он будет установлен на False, два Timer tick сработают ..   -  person Bolu    schedule 09.10.2013
comment
Извините, только что видел ваш комментарий. В этом случае нет ответа, я дважды проверил, что он будет вызываться только один раз, поэтому переместил флаг bool. Я фактически устанавливаю флаг в другом месте кода на основе щелчка по пункту меню, который, как только пользователь по существу запросил MapRedraw, я скрываю его и показываю индикатор занятости, пока карта не будет перерисована. На самом деле это более элегантно, чем я представлял ;-)   -  person MikeyTT    schedule 09.10.2013


Ответы (1)


Этот объект принадлежит потоку ThreadPool, который его создал.

Если вы вызываете Freeze(), его должны использовать другие потоки.

person SLaks    schedule 09.10.2013
comment
Какой объект нужно заморозить? Я посмотрел на другие части своего приложения, которые делают похожие вещи, у которых нет никаких проблем, и я не вижу в этом никакой разницы. Я никогда раньше не использовал Freeze ни для одного объекта. А пока я займусь этим. - person MikeyTT; 09.10.2013
comment
@MikeyTT: Наверное, PathGeometrys. Это проблема, только если вы создаете объект в одном потоке, а затем используете его в другом. - person SLaks; 09.10.2013
comment
Поэтому я проверил и дважды проверил свой другой код, и у меня есть почти точно такие же фрагменты кода в другом месте, у которых нет этой проблемы. Единственная разница в том, что я никогда раньше не использовал PathGeometry. Если у меня есть List ‹float› или ObservableCollection другого класса, я никогда не сталкиваюсь с этой проблемой, даже если я вызываю new ... (); в теме. Похоже, это что-то связано с PathGeometry. В любом случае, как только я заморозю объект, я смогу получить к нему доступ. Я уверен, что сейчас столкнусь с проблемой, я работаю с замороженным объектом, но, эй, маленькие шажки. Спасибо за указатель ... - person MikeyTT; 09.10.2013
comment
@MikeyTT: это применимо только к объектам, имеющим сходство с потоками (все, что наследует DispatcherObject). Обычные коллекции не вызывают эту ошибку. Обратите внимание, что они по-прежнему не являются потокобезопасными; они просто не сообщают вам об этом заранее. Будь осторожен. - person SLaks; 09.10.2013
comment
Осторожный? вы никогда не видели мой настоящий код. Уверен, вам будут сниться кошмары ;-) Приятно знать, что, наверное, я немного волей-неволей относился к этим вещам, TBH. Я не зарабатываю кодом на жизнь, просто увлекаюсь. - person MikeyTT; 09.10.2013