Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cross-Thread Exception - Environment Only

A control can only be accessed by the thread that created it - this much I know.

  1. I have a DataGridView with a DataSource based on a BindingList<>.
  2. I have a worker thread (non-GUI) that runs some fancy calculations/comparisons/etc and then adds/edits an object to/in the BindingList<>.
  3. On a timer, the GUI thread refreshes itself against the BindingList<>.

This code works flawlessly - as long as I'm not running in the environment. In the environment when the .Add() method is called on the BindingList<> I get this handy little error:

An Exception has occurred
EXCEPTION : Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on.
IN METHOD : get_Handle
AT LINE   : 0
CLASS     : System.Windows.Forms.Control

Notice how the name of the control being violated is blank... I would think that if the problem was with updating the BindingList<> it wouldn't matter if I was running in the environment or not. Notwithstanding, that's what I'm seeing. Moreover, the .Add() completes successfully even though the exception is thrown!!

Obviously, it's not a big deal in my production environment (yet?) since it only happens in Studio; and yes I could Invoke the GUI thread to perform the Add, or store the adds in a place for the GUI thread to retrieve them later... I'm not looking for a work-around but more so am interested in the answer to this question:

Why does the error only appear in studio?

like image 247
Chris Barlow Avatar asked Jun 01 '11 19:06

Chris Barlow


3 Answers

The error may occur only in VS if it is a MDA (Managed Debugging Assistant), not a runtime exception. MDAs are there to tell you when you are doing something that is usually, but not 100% always, something that will get you in trouble in production code (which this will, eventually, even if it seems to work 99% of the time on your machine).

You should be invoking to the UI thread to perform the Add method.

EDIT: To be 100% thorough... Without Reflector (since mine expired- are you listening Red Gate?!) I am guessing that the Control class checks to see whether you are on the UI thread, throws the exception on the background thread if you are not, and then redraws the UI anyway. Since the background thread has already added the item, the UI redraw sees it and draws it to the UI as expected, but your background thread is still seeing the exception, and you are either swallowing it silently in a catch block or the background thread is being terminated (which in your app is maybe tolerable, for inst. that BG thread is a threadpool thread).

like image 103
Chris Shain Avatar answered Sep 23 '22 20:09

Chris Shain


The error only appears in VS because it's a kind of diagnostic message trying to tell you that you have a bug. And in production those additional checks are disabled by default. So the bug is always there, but you're only notified while debugging.

I'm not too familiar with multithreaded WinForms programming, but I assume that since you call add on that binding list in another thread the change notifications happen in that other thread too, and since you bound a control it will change it's state during such a notification. Which means that the control is accessed from a wrong thread, which is a bug.

like image 36
CodesInChaos Avatar answered Sep 22 '22 20:09

CodesInChaos


I think it is happening all the time; however, binding errors are usually silently ignored (for example, BindingSource.DataError ). Visual Studio catches exceptions that are handled - to highlight fail.

Having a data-bound control (DataGridView) and binding-aware collection is asking for trouble if you then update that from another thread. The "observer" nature leads it to do updates on the UI thread. I have hacked around this in the past, but I kinda think you should disable the data-binding completely and update manually. And don't forget that manual update and the background edits should both synchronise access so they don't fight.

Depending on how exactly you are doing the binding, you should be able to disable it and manually refresh it in a loop.

like image 40
Marc Gravell Avatar answered Sep 22 '22 20:09

Marc Gravell