Foreach error in thread - c#

I' have a timer that fires the following code. I need to show a form if the are a notice to show. The problem is that when thread is fired inside the thread and i set ConfiguracioGlobal.DicNotices[kvp.Key].Shown = true; the next run don't know that .Shown is true. When showing the form I get the following error: Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on. I think I have to use delegates but I have no idea :(
System.Threading.Thread thread = new System.Threading.Thread(new System.Threading.ThreadStart(delegate
{
try
{
foreach (KeyValuePair<int, Notice> kvp in GlobalConfiguration.DicNotices)
{
if (!kvp.Value.Shown && DateTime.Now.Hour == kvp.Value.HourIni.Hour && DateTime.Now.Minute == kvp.Value.HourIni.Minute)
{
GlobalConfiguration.DicNotices[kvp.Key].Shown = true;
FrmNotices frmPopup = new FrmNotices(kvp.Key);
frmPopup.Show();
Application.Run(frmPopup);
}
}
}
catch (Exception ex)
{
MessageBox.Show("Error");
}
}));
thread.IsBackground = true;
thread.Start();
Any idea?
Thanks to all.

You're trying to have multiple UI threads, and beyond that your second UI thread is neither an STA thread nor a foreground thread (both are a problem) and finally, based on your error, it appears that you're accessing controls from the improper UI thread.
Just don't do this. Only use one UI thread and do all of your work from within that.
Don't start up a background thread to run these forms, show them all in the UI thread that you already have. If you want to ensure that only one form is shown at a time use ShowDialog, and if you want multiple forms to be shown and active simultaneously then use Show instead.

As long as they are STA threads, in theory you can have as many as you want to:
for(var i = 0; i < 4; i++)
{
var t = new Thread((s) =>
{
Application.Run(
new Form
{
Text = s.ToString()
});
});
t.IsBackground = true;
t.SetApartmentState(ApartmentState.STA);
t.Start(i);
}

Related

Call a method running on another thread

I am creating multiple different threads from a single manager class. I now need to update each thread when a variable in the parent application changes.
So I would like to be able to call a function within the thread. What is the best approach? The thread has a reference to the manager class so I was thinking to poll a function in the manager from within the thread to check for changes to the variable, but that doesn't seem right, surely there is a better way. I looked at Dispatcher class but this didn't seem right either. Can anyone recommend the best approach to this?
account1 = new Account(this);
account2 = new Account(this);
Thread accountThread1 = new Thread(new ThreadStart(account1.Run));
Thread accountThread2 = new Thread(new ThreadStart(account2.Run));
accountThread1.Start();
accountThread2.Start();
// How do I call method on accountThread1 or 2?
If i understand it right, you want a proper way to handle the threads.
I couldnt give to you the proper way, but this is the way im handling my threads to get data from PLC 24/7 without crash.
Sample as below:
//Create list of thread
private List<Thread> threads = new List<Thread>();
private void RunThread(YourClass yourclass)
{
switch (yourclass.ConnStat)
{
case ConnStatus.DISCONNECTED:
{
Thread oldthread = threads.FirstOrDefault(i => i.Name == yourclass.ID.ToString());
if (oldthread != null)
{
//Clean old thread
threads.Remove(oldthread);
}
else
{
//Add event here
yourclass.OnResponseEvent += new EventHandler<YourClass.ResponseEventArgs>(work_OnResponseEvent);
yourclass.OnNotificationEvent += new EventHandler<YourClass.NotificationEventArgs>(work_OnInfoEvent);
}
try
{
//Add thread to list
Thread thread = new Thread(new ThreadStart(() => yourclass.Initialize())) { Name = yourclass.ID.ToString(), IsBackground = true };
thread.Start();
threads.Add(thread);
Thread.Sleep(100);
}
catch (ThreadStateException ex)
{
MessageBox.Show(ex.Message);
}
break;
}
case ConnStatus.CONNECTED:
{
MessageBox.Show(string.Format("ID:{0}, is currently running!", yourclass.ID));
break;
}
case ConnStatus.AWAITING:
{
MessageBox.Show(string.Format("ID:{0}, is currently awaiting for connection!", yourclass.ID));
break;
}
}
}
//To control your class within thread
private void StopThread(YourClass yourclass)
{
if (yourclass.ConnStat == ConnStatus.CONNECTED || yourclass.ConnStat == ConnStatus.AWAITING)
{
//Call your method
yourclass.Disconnect();
yourclass.OnResponseEvent -= work_OnResponseEvent;
yourclass.OnDBResponseEvent -= work_OnDBResponseEvent;
yourclass.OnNotificationEvent -= work_OnInfoEvent;
Thread oldthread = threads.FirstOrDefault(i => i.Name == yourclass.ID.ToString());
if (oldthread != null) threads.Remove(oldthread);
}
}
Hope this helps
So the solution to this is to use an Observer Pattern to effectively push variable changes into the threads achieving the required results.
Using an Observer Pattern all the running threads can watch a variable in parent application for changes. This way the threads do not poll the parent, but the threads can respond to changes in a variable in the main application, effectively allowing push of changed variables into all of the threads.
The MS Docs link is a bit complex. I found this was quite a nice easy to follow tutorial:
https://www.youtube.com/watch?v=grHxRCtETP8

Suspending a thread in a wpf application

I have a WPF application in which i have this class :
public partial class Global : UserControl
{
public static List<Thread> listofthreads = new List<Thread>();
public Global()
{
InitializeComponent();
Thread windowThread = new Thread(delegate() { verifing(); });
listofthreads.Add(windowThread);
windowThread.Start();
}
public void verifing()
{
if (Global2.Pat_pathregfile.Length > 5 && Global2.Pat_pathcalibfile.Length > 5) {
if (utilisation.Dispatcher.CheckAccess())
{
utilisation.Visibility = Visibility.Visible;
}
else
{
utilisation.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)(() =>
{
utilisation.Visibility = Visibility.Visible;
}));
}
foreach (Thread t in listofthreads) {
try
{
t.Suspend();
}
catch { }
}
}
else {
if (utilisation.Dispatcher.CheckAccess())
{
utilisation.Visibility = Visibility.Hidden;
}
else
{
utilisation.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)(() =>
{
utilisation.Visibility = Visibility.Hidden;
}));
}
Thread windowThread = new Thread(delegate() { verifing(); });
windowThread.Start();
listofthreads.Add(windowThread);
}
}
}
i need to kill properly all of the threads that i have used
foreach (Thread t in listofthreads) {
try
{
t.Suspend();
}
catch { }
}
But the program indicates that the use of the method suspend isn't recommended .
Why?
It seems that some threads is still working even after the close of the windows, Why this happens? How can i fix it?
Is another method of killing a thread exist in wpf?
1) Why?
Suspend method has been marked as Obsolete by Microsoft. Error states itself:
Thread.Suspend has been deprecated. Please use other classes in
System.Threading, such as Monitor, Mutex, Event, and Semaphore, to
synchronize Threads or protect resources.
2) It seems that some threads is still working even after the close of
the windows, Why this happens? How can i fix it?
You have started all threads as foreground thread which won't stop automatically when main thread finishes its execution. In case you want to stop all threads once all foreground threads stops, you should mark thread as background thread.
windowThread.IsBackground = true;
3) Is another method of killing a thread exist in wpf?
Use Thread.Abort(). However, closing your main thread will automatically stop all background threads (IsBackground set to true on thread), you should not worry about killing them.
What are you trying to do? You are creating threads just for the purpose of checking some condition? And when the condition is true, you change the visibility and block all threads(!) from further execution. And when the condition is not true, you create another thread that does the same. Why are you suspending all threads (including the active one) instead of just letting it terminate? And if you want to periodically check for a condition, use a timer or a wait event instead.
Just as a side note: Your foreach-loops will eventually throw InvalidOperationException, because you're changing the collection without a lock.
And then, don't try to kill threads. Use flags or signals instead. Any attempt to kill threads is a) bad design and b) prone to errors and unexpected behavior.

How to shut down a second UI thread

I need to be able to start up a window on a second UI thread and shut it down again at will.
This is my current code:
/// <summary>Show or hide the simulation status window on its own thread.</summary>
private void toggleSimulationStatusWindow(bool show)
{
if (show)
{
if (statusMonitorThread != null) return;
statusMonitorThread = new System.Threading.Thread(delegate()
{
Application.Run(new AnalysisStatusWindow(ExcelApi.analyisStatusMonitor));
});
statusMonitorThread.Start();
}
else
{
if (statusMonitorThread != null)
statusMonitorThread.Abort();
statusMonitorThread = null;
}
}
AnalysisStatusWindow is a fairly basic System.Windows.Forms.Form
The above code is successfully creating the new UI thread, but my request to Abort the thread is ignored. The result is that toggling the above function multiple times simply results in new windows opening up - all of which are on their own thread and fully functional.
Is there any way I can pass a message to this thread to shut down nicely? Failing that, is there any way to make sure Abort() really kills my second UI thread?
I've tried using new Form().Show() and .ShowDialog() instead of Application.Run(new Form()), but they aren't any easier to shut down.
If anyone is questioning the need for a separate UI thread, this code exists in an Excel Add-in, and I cannot control the fact that the Excel UI blocks while calculations for a given cell are underway. For that reason, when a long running custom formula executes, I require this second UI thread to display progress updates.
Thanks to Hans for his comment. I solved my problem using the following code:
/// <summary>Show or hide the simulation status window on its own thread.</summary>
private void toggleSimulationStatusWindow(bool show)
{
if (show)
{
if (statusMonitorThread != null) return;
statusMonitorWindow = new AnalysisStatusWindow(ExcelApi.analyisStatusMonitor);
statusMonitorThread = new System.Threading.Thread(delegate()
{
Application.Run(statusMonitorWindow);
});
statusMonitorThread.Start();
}
else if (statusMonitorThread != null)
{
statusMonitorWindow.BeginInvoke((MethodInvoker)delegate { statusMonitorWindow.Close(); });
statusMonitorThread.Join();
statusMonitorThread = null;
statusMonitorWindow = null;
}
}

The calling thread cannot access this object error

I am trying to create dynamically custom userControl in background thread.
This is my method where I am creating new thread:
var thread = new Thread(CreateItemInBackgroundThread);
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
And this is method CreateItemInBackgroundThread:
var uc = new MyUserControl();
UserControl item = uc;
AllControls.Add(item);
//Here I am trying to add control to a current Tab
foreach (var currentTab in _allTabs)
{
currentTab.DocumentWindow.Dispatcher.BeginInvoke(new Action(() =>
{
if (tab.DocumentWindow.IsSelected)
{
tempTab = tab;
tempControl = item;
finish = true;
}
}));
}
This is my finish property
bool finish
{
get { return _finish; }
set
{
_finish = value;
if (_finish)
{
tempTab.AnimatedCanvas.Dispatcher.BeginInvoke(new Action(() => tempTab.AnimatedCanvas.Children.Add(tempControl)));
}
} // Here I get error - The calling thread cannot access this object because a different thread owns it
}
How can I avoid this error and why this error happend?
as the error says, you can't access the this object because a different thread owns it, so you can invoke that thread using Invoke(Delegate Method)
you can check if invoke required using tempTab.InvokeRequired
This error is coming because u must be doing the different tasks on the same thread like U cannot make a thread go async and update the UI using the same thread.It will cause conflict.because UI thread is the main thread.
You can use BAckground Worker Thread and subsribe its two eventHandlers to your events which you want to work on.. for eg-
BackgroundWorker Worker=new BackgroundWorker();
worker.DoWork+=Yorevent which will do the timeTaking Task();
Worker.RunWorkerCompleted+=YOurEvent which will Update your UI after the work is done();
worker.RunWorkerAsync();
RunWorkerAsync() will make your thread go async and work on background
this way it will not cause any thread Error too..

BackgroundWorker thread must be STA

I have a BackgroundWorker to call a function to do a long process at BackgroundWorker _DoWork, when error occur in the function I will prompt a customized messagebox:
WPFMessageBoxResult result = WPFMessageBox.Show("Activation Fail", "Error!!", WPFMessageBoxButtons.OK, WPFMessageBoxImage.Error);
The exception below happens at WPFMessageBoxResult class :
The calling thread must be STA, because many UI components require this.
Thank you.
You should not try to interact with any UI components from a background thread.
One way could be to catch the exception in your doWork method and assign it to the backgroundworker's result property and then check if that result is a type of exception or not null if you are not using the result for anything else. then check for it in the backgroundWorker_completed event.
BackgroundWorker_DoWork(sender, )
{
try
{
// do work
}
catch (Exception ex)
{
BackgroundWorker w = sender as BackgroundWorker;
if (w != null)
w.Result = ex;
}
}
then
BackgroundWorker_Completed()
{
if (s.Result != null && e.Result is Exception)
{
Exception ex = e.Result as Exception;
// do something with ex
}
}
Usually with Winforms/WPF you use Invoke() to jump onto the UI thread if you need to interact with the UI from a long-running task. You can call invoke from any object that belongs to the UI, but be sure when in the invoke scope to only do as little code as possible. Since this code is on the UI thread it will block/hang the UI if it takes too long.
public void BackgroundWorkerMethod()
{
try
{
// do work
}
catch (Exception e)
{
uiObject.Invoke(new Action(() => {
// now you are on the UI thread
Message.ShowMessage(e.Message);
});
}
}
Your background thread is just a worker thread and not a user interface thread. WPF and WinForms both require that the thread performing user interface actions be marked as STA (Single Threaded Apartment) because the user interface code is not thread safe. They also require that you add a message pump so that windows messages are dispatched.
I recommend that instead of showing the message box in your worker thread you send a message to your main user interface thread and have that thread show the message box. To do this you should pass a reference to the Dispatcher from the main UI thread into the worker thread. Then use the Dispatcher.BeginInvoke to request a delegate be called back on the main thread.
Alternatively you can wait for the background thread to complete and then check the result and show the appropriate answer to the user. Either way the worker thread should not be directly interacting with the user interface.
You must use this method
void BGW_DoWork(object sender, DoWorkEventArgs e)
{
try
{
Dispatcher.BeginInvoke(new Action(() =>
{
Button btn = new Button();
btn.Width = 100;
btn.Height = 50;
btn.Content = "Test";
myG.Children.Add(btn);
}
));
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}

Categories

Resources