Gabriel G. Roy
Gabriel G. Roy

Reputation: 2632

Is it possible to access a UI element from another thread if the thread doesn't modify that element?

Let's assume that the code running in the thread that instantiated the form/control/element (usually the main thread) does not modify/access that element at the same time, is it possible to:

  1. get the Text property of a TextBox.

  2. enumerate a ListView.

  3. subscribe to a Form's Closing event. (Knowing that the hook will be called from the thread that instantiated that form)

I have tried all 3 and the program doesn't seem to complain about it. I had always thought that you had to invoke any call that wants to even remotely touch anything UI related (read or write).

I understand very clearly why I need to use the IsInvokeRequired/Invoke pattern when modifying an element, but I cannot see why accessing properties/events would cause any problems.

Upvotes: 3

Views: 530

Answers (3)

Brian Gideon
Brian Gideon

Reputation: 48959

This is not safe. The UI thread could (and probably will) change the state of the control while you are reading it from another thread. Your reads might catch the control in a half-baked state. It may appear to be working now, but sooner or later it will fail...probably unpredictably and spectacularly.

No, you probably do not need to use the Invoke pattern. To be quite frank...that pattern is usually the worst option. It is usually better to have your worker thread do the heavy lifting and then send new data or progress information to the UI thread via a queue and let the UI thread pick this up via a timer. This has several advantages.

  • Eliminates the expensive Invoke and BeginInvoke operations.
  • The UI thread gets to decide when and how often it should update itself with new data instead of having the worker thread dictate this.
  • You get more throughput on the worker thread since it doesn't have to wait for Invoke to return.
  • There is no risk of overrunning the UI message queue like there would be with BeginInvoke.
  • It decouples the UI and worker thread interactions.

You will need to enumerate the ListView and build up a separate data structure that can then be accessed by the worker thread safely. If the ListView contains a lot of items consider maintaining a separate collection in tandem with the control. That way you spread out the penalty of coping the data over a bigger period of time in which it probably will not be noticed as much. Afterall, we do not want the copy operation to freeze the UI thread otherwise a user will notice. A ConcurrentBag or the like might be a good choice since it can be safely modified by the UI thread while the worker thread is reading it.

Upvotes: 2

James
James

Reputation: 82136

It's definitely possible, however, it could lead to unexpected behaviour. Also, other thread-related bugs also need to be taken into consideration e.g. race conditions/deadlocks see Managed Threading Best Practises.

I would always stick to accessing the UI on the UI thread to be on the safe side.

Upvotes: 5

Servy
Servy

Reputation: 203825

And what are you doing to ensure that the UI thread isn't modifying the control while you're reading from it? The whole reason for marshaling to the UI thread is so that you don't need to worry about that case. Enumerating the listbox in particular is going to be the easiest to break as it will take the longest time (this creating the largest window for a race condition). You should be marshaling to the UI thread for all 3 of those things.

Upvotes: 3

Related Questions