I am using C#,.Net4.0 and Visual Studio 2010 and I am trying to get this behaviour from my windows form application when the user click on a button:
A GUI LED starts to blink
A long rung operation starts
When the operation at point 2 ends the LED stops blinking
private void Btn_WebService_Click(object sender, EventArgs e)
{
Thread_Blink = new Thread(() => { LED_Blink(LED.WS); });
Thread_Blink.Start();
// Do something that takes time.. Let's imulate with a sleep
Thread.Sleep(2000);
Thread_Blink.Abort();
}
I also tried using 3 different events and/or timers..
private void Btn_WebService_MouseDown(object sender, MouseEventArgs e)
{
Timer_Blink.Enabled = true;
}
private void Btn_WebService_Click(object sender, EventArgs e)
{
// Do something that takes time.. Let's imulate with a sleep
Thread.Sleep(2000);
}
private void Btn_WebService_MouseUp(object sender, MouseEventArgs e)
{
Timer_Blink.Enabled = false;
}
The result is always the same: The LED starts to blink only AT THE END of the long running operation (Thread.Sleep(2000);) and suddenly STOPS so that you can't see anything. Why does this happen and how can I get the desired behaviour?
I add further infos. I tried to use BackgroundWorked and implemented the wanted behavour in this way:
private void Btn_WebService_Click(object sender, EventArgs e)
{
BlinkWorker.RunWorkerAsync(LED.WS);
LedOn(LED.WS);
TestWebService(); // This method takes about 2 seconds to answer..
LedOff(LED.WS);
BlinkWorker.CancelAsync();
}
While the TestWebService() is running I get the LED off(LedOn(LED.WS);). When the TestWebService()has finished the LED comes on.
The BlinkWorker still does not work if started and cancelled inside the Click event.
FYI: If I make a button that starts the blinking and another button that stops it, it works perfectly.
The problem is, you're tying up the UI thread inside that Thread.Sleep. The UI thread is special - it's the only one that can make changes to the UI - so whilst it's busy sleeping, it's not able to service any attempts from your BackgroundWorker or LED_Blink timer callback to change the UI state.
So, you need to not tie up the UI thread. You need to put the Thread.Sleep code (or it's actual real equivalent) into the BackgroundWorkers DoWork handler, or use other means to avoid blocking the UI thread.
Common approaches today would be to make use of async/await if the real work you're trying to do already offers an asynchronous alternative (e.g. await Task.Delay(2000); would be the equivalent of your current Thread.Sleep(2000);). Unfortunately, using async and await would require you to move to a later version of .NET/Visual Studio - but you ought to be considering that anyway. 2010 is quite dated (and also, IMO, probably the worst one to stop on - it was notoriously slow), and .NET 4.0 (as opposed to .NET 4.5.2 or later) is no longer supported.
I would propose that you take a look at the BackgroundWorker class and the ReportProgress method.
Related
I'm trying to use background worker to update a text label continuously, but for DoWork, if no loop used, it will only execute once, but if infinite loop is used, it freezes the GUI, any idea to solve this? Really appreciate!!! I'm pretty new to C# and still trying to learn.
Here's my code:
This in the main form:
backgroundWorkerX.DoWork += backgroundWorkerX_DoWork;
backgroundWorkerX.ProgressChanged += backgroundWorkerX_ProgressChanged;
backgroundWorkerX.WorkerReportsProgress = true;
backgroundWorkerX.RunWorkerAsync();
Then:
public void backgroundWorkerX_DoWork(object sender, DoWorkEventArgs e)
{
X = -(RSSI_PI1_ST1);
backgroundWorkerX.ReportProgress(X);
}
public void backgroundWorkerX_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label9.Text = e.ProgressPercentage.ToString();
}
public void backgroundWorkerX_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
}
[...] if no loop used, it will only execute once
This is the expected behavior. It will do the thing it's expected and then call RunWorkerCompleted.
[...] but if infinite loop is used, it freezes the GUI.
You don't present a loop in your question. So I'll make an assumption and will give you an educated guess that it actually consumes all the CPU, and therefore crashes the GUI.
// I assume that you loop like this somehow.
do
{
X = -(RSSI_PI1_ST1);
backgroundWorkerX.ReportProgress(X);
} while (true);
Try to add something to "slow it down" once in a while. Otherwise it will just run and run as fast as possible, and therefore consume all the CPU available. Remember that everytime you use ReportProgress it will be rendered by the GUI-thread. And this will execute very often and not leave a lot of resources for the GUI thread to respond to other things. Below I use Thread.Sleep, which will pause the thread for half a second. But remember that you will only want to do this in a background thread.
do
{
X = -(RSSI_PI1_ST1);
backgroundWorkerX.ReportProgress(X);
Thread.Sleep(500);
} while (true);
I would also consider doing this another way. If you actually have the need of pausing the thread, you might as well do it with a Timer. Thread.Sleep will lock the thread, and may therefore not be what you really want. From this answer:
Process() // method to be called after regular interval in Timer
{
// lengthy process, i.e. data fetching and processing etc.
// here comes the UI update part
Dispatcher.Invoke((Action)delegate() { /* update UI */ });
}
i have a search thread in my project .
the thread is created in 'Form1()' function:
objSearchThread = new Thread(this.Thread_Func);
when user clicks the 'search' button, Start() function is called:
private void Button_Search_Click(object sender, EventArgs e)
{
objSearchThread.Start();
}
second clicked of this button, crashed! because the thread is 'Started' state.
if i change my button clicked code, and i add 'new' command. it works without error or crashing:
private void Button_Search_Click(object sender, EventArgs e)
{
objSearchThread = new Thread(this.Thread_Func);
objSearchThread.Start();
}
doesn't it need to delete thread object(objSearchThread )?
does it need to call Abort() or other functions, when thread working ends?
is second code that i write here correct?
No, you don't need to do anything. The thread will just finish when it has no more work to do. You might want to consider scheduling it to execute on the thread pool however, instead of creating a new thread each time. You could do that directly, or via the Task Parallel Libray (TPL) with the Task API. Alternatively, you might want to use BackgroundWorker, as that makes it easier to report progress to the UI. (Depending on what you're doing, you may not even need another thread at all - if you're calling a web service for example, you may be able to use the async facilities in C# 5 to make the code simpler and more efficient in one go...)
If you're using Thread directly, however, you do need to create a new Thread object each time you want to start it, as you can't reuse a thread.
Additionally, unless you need this to be an instance variable, you should consider making it a local variable. When do you ever access the thread other than in method?
Well, I've been struggling with this for a while and I've been unable to find a solution.
I'm developing one of those cashier apps you see in the supermarket where everything works fast as hell. The cashiers know the program so good they just type at the speed of light so the UI must be incredibly responsive.
Anyway, as I've only coded in WPF I'm using it, if someone says WinForms is faster I'll surely learn it.
This is my code, it's very simple as I'm testing performance
public partial class MainWindow : Window
{
Thread t;
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
t = new Thread(HandleThread);
t.Start();
}
private void HandleThread()
{
Thread.Sleep(3000);
}
}
Whenever the button is clicked, a new thread is launched as I plan on connecting to a server in the new thread.
My problem is the method button_Click is VERY SLOW. It takes about 1 second to return so the button looks like it's stuck and the application look sluggish. I've tried threads, BackgroundWorker, changed the framework to 4.0 and tried Tasks. NOTHING. Whatever I do to start some kind of background work the method takes forever.
I need to check the username and password on a remote server, how can I accomplish it without hurting the UI?
You should turn off the IntelliTrace (How-To).
On the Tools menu, click Options.
In the Options dialog box, expand the IntelliTrace node and then click General.
Clear the Enable IntelliTrace check box.
Click OK.
Do you need to spawn a new thread every time you click your button? Is this thread long-lived? Do you really want to allocate 1 meg of stack space (on 64 bit OSes) for this every time you click this button? Really sure you don't want to use the TPL's Task and CancellationTokenSource instead?
In your case, you should really not create a thread every time you click, what you want is start a long-running async task and check the result.
public void OnClick(object src,EventArgs args)
{
var login = tbLogin.Text;// assuming non MVVM coding here
var pwd= tbPass.Text;
Task.Factory.StartNew(()=>{
return _myWebService.CheckAuth(login,pwd); // your login stuff here
}).ContinueWith(wsTask=>{
if(!wsTask.IsCompleted){ // handle errors / cancel }
DisplayLoginState(ws.Result);
}, TaskScheduler.FromCurrentSynchronizationContext()); // this runs on the UI Thread
}
I have a simple Windows Forms app that uses a couple BackgroundWorker elements to, well, do things in the background. While developing, I noticed I was able to do things such as get and set the .Text value of a Label in the DoWork() method, but am not able to get the value of a DrowDownList.
I was under the impression that the BackgroundWorker is not supposed to have any UI interactions, are there some exceptions to this rule?
I'm not looking for suggestions to update the UI from the BackgroundWorker, I am more curious if there are shortcuts I can take, like updating the text of a ToolStripStatusLabel directly from the BackgroundWorker.
Here is a simple example, worker 1 updates UI directly while worker 2 uses progress updates:
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void button2_Click(object sender, EventArgs e)
{
backgroundWorker2.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
toolStripStatusLabel1.Text = "Performing task 1...";
performTaskOne();
toolStripStatusLabel1.Text = "Task 1 done.";
toolStripStatusLabel1.Text = "Performing task 2...";
performTaskTwo();
toolStripStatusLabel1.Text = "Task 2 done.";
toolStripStatusLabel1.Text = "Performing task 3...";
performTaskThree();
toolStripStatusLabel1.Text = "Task 3 done.";
}
private void backgroundWorker2_DoWork(object sender, DoWorkEventArgs e)
{
backgroundWorker2.ReportProgress(0, "Performing task 1...");
performTaskOne();
backgroundWorker2.ReportProgress(20, "Task 1 done.");
backgroundWorker2.ReportProgress(40, "Performing task 2...");
performTaskTwo();
backgroundWorker2.ReportProgress(60, "Task 2 done.");
backgroundWorker2.ReportProgress(80, "Performing task 3...");
performTaskThree();
backgroundWorker2.ReportProgress(100, "Task 3 done.");
}
private void backgroundWorker2_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
string status = (string)e.UserState;
toolStripStatusLabel1.Text = status;
}
private void performTaskThree()
{
Thread.Sleep(1000);
}
private void performTaskTwo()
{
Thread.Sleep(1000);
}
private void performTaskOne()
{
Thread.Sleep(1000);
}
You should not be accessing or updating the UI from a background thread. Period.
It's a feature of most UI elements that they will detect if they are not being access from the UI thread and aggressively throw an exception so that the developer becomes immediately aware the first time the code runs that they are accessing the control improperly.
Not all UI controls, and not all aspects of UI controls, have this feature. Certain members are specifically documented as a member of the object that is safe to use from a non-UI thread (for example the Invoke method of a control can obviously be used from a non-UI thread; that's why it exists). For those not documented as such, you should not be using them in non-UI threads.
Just because it doesn't crash the first time you use it doesn't mean it will work. What it means is that there are likely race conditions throughout the code that can crop up depending on what other threads are accessing shared resources at the same time. It may work when you test it, it might work repeatedly, but if just the right set of circumstances take place, all sorts of bad things can happen. Perhaps the program will crash, perhaps data will be corrupted, perhaps things just won't work, or perhaps all sorts of weird and cooky things will happen. If such code gets set out to clients where you all of a sudden get a lot of different people executing the code in a lot of different and unexpected ways, as well as on very different types of environments, the chances of running into problems not encountered in your own tests goes up dramatically.
So take the time to ensure that you only access UI controls from the UI thread.
It's also worth noting, on an entirely separate note, that it greatly helps to organize your code to have your UI and presentation logic separated from your business logic; mixing them results in applications that are harder to reason about, understand, maintain, or change.
I'm working in a c# windows application with vs2010 and a local database.In one of my forms i use a BindingNavigator with a group of textboxes filled by the database and a ReporViewer. I've added a background worker in order to fill the table adapters in case there are a lot of records in the database.
The problem is that the way I'm using the background worker when i debug my app i cannot see any data in the textboxes, otherwise when i run my app it's working fine. I know that this is a case of accessing the UI on a non-UI thread and it is wrong. Is there a another way around it?Thank you in advance.
Here is the code I'm using:
private void Client_Load(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
this.clientTableAdapter.Fill(this.database1DataSet.Client);
this.projectTableAdapter.Fill(this.database1DataSet.Project);
if (InvokeRequired)
{
this.Invoke(new MethodInvoker(this.reportViewer1.RefreshReport));
return;
}
}
Typically, a background worker returns on the same thread, and would actually throw an exception about the non-UI thread. However, this might be eaten in your case. You should be using the RunWorkerCompleted event for items that are to happen after your main work is done, especially when looking to update the UI. And, this should return to the same thread it was called from (UI in your case) as mentioned above.
So, I would move your UI processing code (RefreshReport) into a new method set up for the RunWorkerCompleted.
However, my suggestion would be to take a look at the Task Parallel Library. It ends up making code much cleaner and easier to debug IMO.
Example (rough and may not compile due to the nulls, but you can get the jist :)):
var task = Task.Factory.StartNew(()=>{//Do Async Stuff});
task.ContinueWith((previousTask)=>{//Do your UI Stuff}, null, null,
TaskScheduler.FromCurrentSynchronizationContext);
//The FromCurrentSync makes sure the method returns
//to the same thread (UI in this case) that it started
I know that is not a direct answer, but more of a suggestion towards what I would consider a cleaner, more debuggable approach.