How do I access variables from a different thread? - c#

Getting error: Cross-thread operation not valid: Control 'label1' accessed from a thread other than the thread it was created on.
Code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Windows.Forms;
namespace WindowsFormsApplication2
{
public partial class Form1 : Form
{
string CONNECTING = "Connecting to server...";
string GETTING_DATA = "Getting data...";
string CONNECT_FAIL = "Failed to connect!";
string CONNECT_SUCCESS = "Connection established!";
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Thread t1 = new Thread(run);
t1.Start();
}
public void run() {
label1.Text = CONNECTING;
}
}
}
How do I properly fix this? I've tried using CheckForIllegalCrossThreadCalls = false; but that obviously causes errors.
I'd also like to know how I can stop the thread, since it can't be accessed anymore outside of that function.
Thanks in advance!

Try using BeginInvoke:
public void run()
{
label1.BeginInvoke(new Action(() =>
{
label1.Text = CONNECTING;
}));
}
Only the UI thread can update UI elements in a Windows Forms app. Other threads need to use BeginInvoke to update the UI.
ORGINAL: i assumed this was a WPF app and said to use this.Dispatcher, but Dispatcher isn't in Windows Forms apps.

Accessing a control from different threads
In WinForms App you can ony access directly a Control from the thread it was created.
To do such a task you will need to use InvokeRequired property of a control to see if you must use Invoke inorder to force a call of the action from the original thread.
A public method that might be accessed from any thread including the original would look like this:
public void run() {
if (label1.InvokeRequired) //Is this method being called from a different thread
this.Invoke(new MethodInvoker(()=> label1.Text = CONNECTING));
else //it's cool, this is the original thread, procceed
label1.Text = CONNECTING;
}
But if you are absolutly sure that run() method will be called only from the thread, consider not even checking if InvokeRequired and immediatly call Invoke
Further information: http://msdn.microsoft.com/en-us/library/ms171728(v=vs.80).aspx
Stopping a thread in progress
Simples is to use t1.Abort(); method of a Thread. This will throw and exception forcing it to stop where ever it was. this is great for threads that do not do any long processing so stopping it won't cause any problems.
If you do do proccesing in your thread, which means you can't just stop it in the middle then I suggest you to use a boolean that will indicate that the thread must cancel ASAP.
private bool isCancelRequired = false;
public void run() {
while(true) {
//do long processing..
if (isCancelRequired)
break;
}
}
More advanced methods: http://www.yoda.arachsys.com/csharp/threads/shutdown.shtml

Related

Calling delegate method blocks my C# app [duplicate]

This question already has answers here:
Unresponsive UI when using BeginInvoke
(3 answers)
Closed 7 years ago.
I have written a program that counts and the value is represented in the label's text.
The process is started by clicking on the button.
When I start, UI freezes.
I want to solve it by delegate.
Where is my bug?
Code
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication6
{
public delegate void MyDelegate();
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public void DelegateMethod()
{
for (int i = 0; i < 9999999999999; i++)
{
label1.Text = i.ToString();
}
MessageBox.Show("OK");
}
private void button1_Click(object sender, EventArgs e)
{
BeginInvoke(new MyDelegate(DelegateMethod));
}
}
}
It was because your UI element is getting updated so frequently and it will keep doing until loop terminates, if you add Thread.Sleep() after every iteration you can see the behaviour different:
for (int i = 0; i < 9999999999999; i++)
{
label1.Text = i.ToString();
// for example delay 1 second
Thread.Sleep(1000);
}
A more better approach is to use async and await keywords introduced in c# which will do extra work in background, not on UI thread, currently all the processing is getting done on your UI thread, which is causing UI thread to get blocked.But in your case that will not make difference, because here the problem is updating UI very fast which causes it to freeze.
It blocks because you are running it on the same thread as the UI. Also, that many updates to a label will not exactly perform very well - even if being updated from another thread.
If you really want to do that, create a new thread/task and invoke the UI update properly from there.
Because you do it on the UI-Thread. Try Task.Run() instead of BeginInvoke().

Update progress for C# WPF application time-taking task

The question is pretty trivial: I need to update progress on WPF application while time-consuming calculation is processed. In my tries, I've made some googling and finally based on the first code snippet from this solution: How to update UI from another thread running in another class. And here's my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Threading;
namespace ThreadTest
{
public class WorkerClass
{
public int currentIteration;
public int maxIterations = 100;
public event EventHandler ProgressUpdate;
public void Process()
{
this.currentIteration = 0;
while (currentIteration < maxIterations)
{
if (ProgressUpdate != null)
ProgressUpdate(this, new EventArgs());
currentIteration++;
Thread.Sleep(100); // some time-consuming activity takes place here
}
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void btnStart_Click(object sender, RoutedEventArgs e)
{
WorkerClass wItem = new WorkerClass();
wItem.ProgressUpdate += (s, eArg) => {
Dispatcher.BeginInvoke((Action)delegate() { txtProgress.Text = wItem.currentIteration.ToString(); });
};
Thread thr = new Thread(new ThreadStart(wItem.Process));
thr.Start();
// MessageBox.Show("Job started...");
while (thr.IsAlive == true)
{
Thread.Sleep(50);
}
MessageBox.Show("Job is done!");
}
}
}
The issue is that if I use Dispatcher.Invoke, than the working thread (thr) gets into WaitSleepJoin state after the first cycle pass and does not resume, therefore the entire application freezes. I've googled several suggestions to use Dispatcher.BeginInvoke instead, but in this case the progress is not updated untill the process finishes the work. I guess the issue is related to switching between threads, but cannot get exact point.
This is a classic "Invoke deadlock" scenario. Stack Overflow has a number of existing questions addressing this problem for Winforms, but I could only find one related question in the WPF context (Deadlock when thread uses dispatcher and the main thread is waiting for thread to finish), but that one isn't precisely the same question, or at least doesn't have answers (yet) that would directly address your question, and the question and answers themselves are poorly enough formed that I feel your question warrants a fresh answer. So…
The basic issue is that you have blocked your UI thread waiting on the process to finish. You should never do this. You should never block the UI thread for any reason. It should be obvious that if you wait for any reason in code running in the UI thread, then the UI itself cannot respond to user input or do any sort of screen refresh.
There are lots of possible ways to address this, but the current idiom for dealing with this would be to use async and await, along with Task to run your process. For example:
private async void btnStart_Click(object sender, RoutedEventArgs e)
{
WorkerClass wItem = new WorkerClass();
wItem.ProgressUpdate += (s, eArg) => {
Dispatcher.BeginInvoke((Action)delegate() { txtProgress.Text = wItem.currentIteration.ToString(); });
};
Task task = Task.Run(wItem.Process);
// MessageBox.Show("Job started...");
await task;
MessageBox.Show("Job is done!");
}
Note that while the above declares the async method as void, this is the exception to the rule. That is, normally one should always declare async methods as returning Task or Task<T>. The exception is situations such as this, where the method is an event handler and so is required to match an existing signature where the method must be declared to return void.
I ran your code as it is, and commented out this :
while (thr.IsAlive == true)
{
Thread.Sleep(50);
}
Everything worked as expected.
/Edit after user comment/
To get notified of processing completion, do these changes :
a. public event EventHandler ProgressCompleted; in your WorkerClass.
b.
if (ProgressCompleted != null)
ProgressCompleted(this, new EventArgs());
after your while finishes in Process() method.
c. In your BtnStart_Click before creating thread.
wItem.ProgressCompleted += (s1, eArgs1) =>
{
MessageBox.Show("Job is done!");
};

Cross Thread Operation - Invoked function calls another function C#

I have a program which uses many threads, when one of the threads found an answer (I think the context doesn't really matter) - it announces it, and then the first thread I created calles a function in a user control class using Invoke.
I checked - and if I change any attributes in this function, I do not get the cross-thread operation. But this function starts a timer (System.Timers.Timer) -> so the function of the "Elapsed" event is called. Inside it I am trying to change an attribute, and that causes cross-thread operation. What am I doing wrong? Isn't it possible to have the invoked function calling another function and then change the attributes in there?
By the way, is it wrong to invoke functions using the delegate? I mean, having the delegate as an attribute of the class I need it in, and then using delegAttributeName.Invoke(parameters) - and not this.Invoke(new Delegate(), parameters);
Heres part of the code:
Thats is where I invoke the function:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace Nim_Assignment_3
{
public delegate void drawDeleg(Color c, int amount, int rowNumber);
public partial class Nim : Form
{
private event drawDeleg myDrawDeleg;
private void CheckXor()
{
if (this.foundToPaint)
{
this.myDrawDeleg.Invoke(this.currentTurnColor, this.amountToPaint, this.rowToPaint);
this.drawWait.WaitOne();
this.foundToPaint = false;
if (this.currentTurnColor == Color.Blue)
this.currentTurnColor = Color.Red;
else
this.currentTurnColor = Color.Blue;
}
}
// the invoked function:
private void callFillPencils(Color c, int amount, int rowNumber)
{
this.rows[rowNumber].fillPencils(c, amount);
}
}
}
And this is the function that the invoked function is calling - and the one it calls (the timer-elapsed event function):
(fillPencils - the function that the invoked function in the Form class (Nim) is calling):
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Timers;
namespace Nim_Assignment_3
{
public partial class PencilsUC : UserControl
{
private PictureBox[] pencils;
public static Image grayPencil = new Bitmap("GrayPen.bmp"), bluePencil = new Bitmap("BluePen.bmp"), redPencil = new Bitmap("RedPen.bmp");
private int amountOfPencils, amountOfPencilsLeft, currIndex, currAmount;
private System.Timers.Timer timer;
private Color currColor;
public event FinishedDrawing drawFinishedDeleg;
public PencilsUC()
{
// intializing things in the constructor...
this.timer = new System.Timers.Timer();
this.timer.Interval = 100;
this.timer.Elapsed += new ElapsedEventHandler(timer_Tick);
}
public void timer_Tick(object sender, EventArgs e)
{
// THE THING THAT MAKES THE CROSS THREAD-OPERATION: THE LINE INSIDE THE "if"
if (this.currColor == Color.Blue)
pencils[currIndex--].Image = bluePencil;
else
pencils[currIndex--].Image = redPencil;
this.currAmount--;
if (this.currAmount == 0)
{
this.timer.Stop();
if (this.drawFinishedDeleg != null)
this.drawFinishedDeleg.Invoke(this, new EventArgs());
}
}
public void fillPencils(Color c, int amount)
{
MessageBox.Show("Hello");
this.currColor = c;
this.currAmount = amount;
this.timer.Start();
}
}
}
(THE CROSS THREAD OPERATION HAPPENS INSIDE THE TIMER_TICK FUNCTION)
I used the windows forms timer at first but for some reason it didn't get to the tick-event function (timer.Start() was called but I put a message box in the tick function and it didnt get in there so I changed it - I saw some answers that said it was better)
I would love some help, I am sorry for the long post, I just wanted to be as clear as I can...
Thanks a lot in advance! :)
Use a Windows.Forms.Timer instead of a System.Timers.Timer. (You'll need to change the names of a few properties/events, i.e. Tick instead of Elapsed, but it's straightforward enough.)
The Timer in the Form's namespace marshals the Tick event into the UI thread, unlike the systems timer which executes the event in a thread pool thread.
If you really prefer to use the system's timer, then you can set the SynchronizingObject to have it marshall it's event to the UI thread:
timer.SynchronizingObject = this;
Note that the UserControl is a synchronizable object.
You need to .Invoke onto the main thread to change any controls.
Image image;
if (this.currColor == Color.Blue)
image = bluePencil;
else
image = redPencil;
this.Invoke(new MethodInvoker(() => pencils[currIndex--].Image = image));
The => is the syntax for a lambda (called anonymous method in other languages). Think about it as a one-line function.
() => pencils[currIndex--].Image = image
is the same as:
void SetImage(Image image, ref int currIndex) {
pencils[currIndex--].Image = image;
}
MethodInvoker provides a simple delegate that is used to invoke a method with a void parameter list
The easiest fix as you already made code would be to set SynchronizingObject of the Timer to Form, so timer would run on UI thread.

Thread error in C# Windows Form

I have gotten this error:
An unhandled exception of type 'System.InvalidOperationException'
occurred in System.Windows.Forms.dll
Additional information: Cross-thread operation not valid: Control
'Redlight' accessed from a thread other than the thread it was created
on.
Redlight and Greenlight are pictureBoxes.
Basically, all I want it to be able to do is alternate between each picture every second.
I searched on this website for similar errors, I see it has to do with "Invoking", but I don't even know what that is, can someone enlighten me?
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
namespace EMCTool
{
public partial class EMCTool_MainForm : Form
{
bool offOn = false;
public EMCTool_MainForm()
{
InitializeComponent();
}
private void EMCTool_MainForm_Load(object sender, EventArgs e)
{
System.Threading.Timer timer = new System.Threading.Timer(new System.Threading.TimerCallback(timerCallback), null, 0, 1000);
}
private void timerCallback(object obj)
{
if (offOn == false)
{
Redlight.Show();
offOn = true;
}
else
{
Greenlight.Show();
offOn = false;
}
}
}
}
You get the Cross-thread error when you try to update a UI element from any thread it was not created on.
Controls in Windows Forms are bound to a specific thread and are not thread safe. Therefore, if you are calling a control's method from a different thread, you must use one of the control's invoke methods to marshal the call to the proper thread. This property can be used to determine if you must call an invoke method, which can be useful if you do not know what thread owns a control.
Refer here for more
Try this .This works fine for me
if (pictureBoxname.InvokeRequired)
pictureBoxname.Invoke(new MethodInvoker(delegate
{
//access picturebox here
}));
else
{
//access picturebox here
}
In WinForms projects better use the System.Windows.Forms.Timer as it calls the Tick event on the UI-thread automatically:
private System.Windows.Forms.Timer _timer;
private void EMCTool_MainForm_Load(object sender, EventArgs e)
{
_timer = new System.Windows.Forms.Timer { Interval = 1000 };
_timer.Tick += Timer_Tick;
_timer.Start();
}
private void Timer_Tick(object sender, EventArgs e)
{
if (offOn) {
Greenlight.Show();
} else {
Redlight.Show();
}
offOn = !offOn;
}
Alternative solution would be to use System.Timers.Timer which has SynchronizingObject property so set this, and it will work:
timer.SynchronizingObject = This
Or to use System.Windows.Forms.Timer as it won't raise exception (it raises Tick event on UI thread).

Threading Basics

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace testThreads
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
public void countToLots()
{
for (int i = 0; i < 10000000; i++)
{
textBox1.Text = "Counting to 10000000, value is " + i + Environment.NewLine;
}
}
public void countToZero()
{
for (int i = 10000000; i > 0; i--)
{
textBox2.Text = "Counting to 0, value is " + i + Environment.NewLine;
}
}
private void button1_Click(object sender, EventArgs e)
{
Thread countUp = new Thread(new ThreadStart(countToLots));
Thread countDown = new Thread(new ThreadStart(countToZero));
countUp.Start();
countDown.Start();
}
private void button2_Click(object sender, EventArgs e)
{
textBox3.Text = "Bobby bob bob " + Environment.NewLine;
}
}
}
I really need to try and get the hang of this - i just dont understand the theory behind why i get an error message. Could someone help me out please?
Cross-thread operation not valid:
Control 'textBox1' accessed from a
thread other than the thread it was
created on.
UI controls have "thread affinity"; they do not want to be touched by anything except the UI thread; that includes reading and writing properties. The assignment to .Text should be done from the UI thread, either by using Invoke, or BackgroundWorker.
For example:
public void countToLots()
{
for (int i = 0; i < 10000000; i++)
{
// running on bg thread
textBox1.Invoke((MethodInvoker) delegate {
// running on UI thread
textBox1.Text = "Counting to 10000000, value is "
+ i + Environment.NewLine;
});
// running on bg thread again
}
}
But note that this type of thread switching has overhead. You should not call back every iteration - you should (for example) update the UI every [n] iterations - in the above, every 10000 for example.
You cannot use a method or property of a Form control from a different thread than the thread that created (called new) the control.
To do that just do:
public void countToLots()
{
for (int i = 0; i < 10000000; i++)
{
SetText("Counting to 10000000, value is " + i + Environment.NewLine);
}
}
public void SetText(string text)
{
if (this.textBox1.InvokeRequired())
{
Action<string> auxDelegate = SetText;
this.BeginInvoke(auxDelegate,text);
}
else
{
this.textBox1.Text = text;
}
}
What the method is doing with the beginInvoke is just calling again the SetText method from the thread that created the control.
Ok, about the theory behind WHY controls have UI Thread affinity.
If you've programmed long enough you would remember the days when forms and rapid application development were not the standard. In those days just droping a control into a form was rare... everything was done by the old school.
Now, in windows, the "old school" way of doing things involved defining a WindowProc.
The WindowProc is a function which is invoked to handle application message (notice I say is, not was). This function runs on the main program thread and is in charge of handling every message the application receives, including user interface paint and refresh.
Nowadays all that is mostly automated so that when you create a form the code in charge of doing all the work is autogenerated and you don't have to worry about that... but it is still there.
Of course, if the thread in charge of drawing the user interface with all its controls, is the main thread, you'll see how changing things from another thread might disturb the application itself with race conditions and so on. In addition, since the UI handling is autogenerated you can't just put the synchronization mechanisms that you'll use with two standard threads because you only have access to code on one thread, yours, but not to the main windowproc callback.
In a way, what BeginInvoke will do for you is pass a message to the main thread telling it to kindly handle the delegate in her own context when the time is right, thus delegating the execution to the main thread.

Categories

Resources