InvalidOperationException: The calling thread cannot access this object because another thread belongs to it.

Possible duplicate:
The calling thread cannot access this object because another thread belongs to it.

Error:

The calling thread cannot access this object because a different thread owns it. 

code:

 public partial class MainWindow : Window { Thread t; bool interrupt; public MainWindow() { InitializeComponent(); } private void btss_Click(object sender, RoutedEventArgs e) { if (t == null) { t = new Thread(this.calculate); t.Start(); btss.Content = "Stop"; } else { t.Interrupt(); } } private void calculate() { int currval = 2; int devide = 2; while (!interrupt) { for (int i = 2; i < currval/2; i++) { if (2 % i != 0) { lbPrimes.Items.Add(currval.ToString()); //Error occures here } } currval++; } } } 

What would cause this, and how can I solve it?

+4
source share
4 answers

You need to connect to the main user interface thread in order to influence the user interface. You can check if this is required using InvokeRequired and implement Invoke before linking to controls.

 private void calculate() { if (InvokeRequired) { Invoke(new Action(() => calculate())); } else { // } } 
+4
source

Access to any user interface element ( lblPrimes here) from a thread other than the UI is not allowed. You must use Invoke from your thread to do this.

Here is a good tutorial:

http://weblogs.asp.net/justin_rogers/pages/126345.aspx

+2
source

You can only update the GUI from the main thread.

In your working method (calculate ()), you are trying to add items to the list.

 lbPrimes.Items.Add(currval.ToString()); 

This throws an exception.

You gain access to a control that is not thread safe. When a thread that has not created a control tries to call it, you will get an InvalidOperationException.

If you want to add items to the list, you need to use InvokeRequired, as stated in TheCodeKing.

For instance:

 private delegate void AddListItem(string item); private void AddListBoxItem(string item) { if (this.lbPrimes.InvokeRequired) { AddListItem d = new AddListItem(item); this.Invoke(d, new object[] { item}); } else { this.lbPrimes.Items.Add(item); } } 

Call this AddListBoxItem (...) method in your Calculate () method instead of directly trying to add items to the list control.

+1
source

The problem is that your workflow is trying to access a user interface element that is not allowed. The exception you receive warns you about this. Often you don’t even understand this. Instead, your application will be unpredictable and spectacular.

You can use Control.Invoke to marshal delegate execution in the user interface thread. This delegate will perform lbPrimes.Items.Add operations. However, I do not recommend this approach in this case. The reason is that it will slow down the workflow.

My preferred solution was for the workflow to add currval to the ConcurrentQueue . Then the UI thread will periodically try out this collection using System.Windows.Forms.Timer to remove the values ​​and put them in the ListBox . This has many advantages over using Control.Invoke .

  • It removes the hard link between the worker and user interface threads that Invoke imposes.
  • He is responsible for updating the user interface in the user interface thread, where he must belong in any case.
  • The user interface flow determines when and how often the update occurs.
  • The workflow should not wait for the user interface to respond to the Invoke request. This will increase the throughput of the workflow.
  • This is more efficient since Invoke is an expensive operation.
  • Many of the subtle race conditions that arise when trying to stop a workflow using Invoke naturally go away.

Here's what my preferred option might look like.

 private void calculate() { int currval = 2; int devide = 2; while (!interrupt) { for (int i = 2; i < currval/2; i++) { if (2 % i != 0) { queue.Add(currval); // ConcurrentQueue<int> } } currval++; } } private void Timer_Tick(object sender, EventArgs args) { int value; while (queue.TryDequeue(out value)) { lbPrimes.Items.Add(value.ToString()); } } 

I noticed a couple more problems.

  • Thread.Interrupt unlocks pending BCL calls such as WaitOne , Join , Sleep , etc. Your use is inappropriate. I think you want to set interrupt = true instead.
  • You should probably interrupt in a for loop instead of a while . If currval gets large enough, it will take longer for the thread to respond to the interrupt request.
0
source

All Articles