In my app I want create thousands files in background, but when I execute this it always freeze my form(after all files were created I can work with my form again). How run it properly without hanging?
private void button5_Click(object sender, EventArgs e)
{
BackgroundWorker bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.DoWork += new DoWorkEventHandler(
delegate(object o, DoWorkEventArgs args)
{
BackgroundWorker b = o as BackgroundWorker;
this.Invoke(new MethodInvoker(delegate
{
getValues();//load some text fields into strings
while (counter < counted)
{
text = richTextBox1.Text;
text = text.Replace("number", finalNumber);
//create copies
if (checkBox1.Checked == true)
{
while (createdCopies < copies)
{
createdCopies++;
File.WriteAllText(fileName, text);
overalCounter++;
b.ReportProgress(overalCounter);
}
counter++;
createdCopies = 0;
}
//dont create copies
else
{
File.WriteAllText(fileName, text);
counter++;
overalCounter++;
b.ReportProgress(overalCounter);
}
//info about number of files created
label6.Text = "created " + overalCounter.ToString() + " files";
}
label1.Text = "success";
}));
});
if (bw.IsBusy != true)
{
bw.RunWorkerAsync();
}
bw.ProgressChanged += new ProgressChangedEventHandler(
delegate(object o, ProgressChangedEventArgs args)
{
this.Text = string.Format("{0}% Completed", args.ProgressPercentage);
});
}
this.Invoke runs the code inside on the UI thread, blocking any UI updates.
Since you run everything in the Invoke method everything will run on the UI thread.
Create a separate Invoke around each place where you modify your UI controls and leave the heavy work outside the Invokes.
Do the actual work in your first delegate. The second delegate this.Invoke(...) executes on the thread of the form (=main thread) and therefore blocks you UI.
Related
In my C# windows form, I want to set notification label with loop and thread before fill the data into data grid view . for that I try to use below method and thread.
My Thread is
public void Run()
{
System.Windows.Forms.MessageBox.Show("this is System Thread");
m = new Dashboard();
string text = "";
Int32 a = 0;
do
{
Thread.Sleep(1000);
notiCount++;
/*Notifications ne = new Notifications(m.loadNotification);
ne.Invoke(text);*/
//Thread.Sleep(1000);
}
while (notiCount <= 10) ;
}
My method is
private void loadINtransfer(string status)
{
Int32 x = 0;
string text="";
SystemThreadings s = new SystemThreadings();
Thread t = new Thread(s.Run);
t.Start();
Thread.Sleep(100);
do
{
if (s.notiCount == 0)
{
text = "Request Data";
Thread.Sleep(1000);
this.loadNotification(text);
// MessageBox.Show("request");
}
else if (s.notiCount == 3)
{
text = "Fetching Data";
Thread.Sleep(1000);
//MessageBox.Show("request");
this.loadNotification(text);
}
else if (s.notiCount == 6)
{
text = "Loading Data";
Thread.Sleep(1000);
this.loadNotification(text);
//MessageBox.Show("loading");
}
else if (s.notiCount == 9)
{
text = "Done";
Thread.Sleep(1000);
this.loadNotification(text);
//MessageBox.Show("done");
}
//Thread.Sleep(200);
}
while (s.notiCount != 10);
//Thread.Sleep(5000);
MessageBox.Show("attach");
dt = dl.loadTransfer(status);
dgvDashboard.AutoGenerateColumns = false;
dgvDashboard.DataSource = dt;
}
but it only show Done part. other if parts are passing but not show. And I want to wait some time the all parts before load data to grid view. how can I do this. Please help Me.
As I alluded to in the comments, this is pretty well a poster-child use case for BackgroundWorker. I created a new Windows Forms application, added a button and a label (I didn't bother to rename them), and then double-clicked the button to add an event handler. After adding the BackgroundWorker code, this is what Form1.cs ended up looking like:
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
var bw = new BackgroundWorker();
bw.DoWork += Bw_DoWork;
bw.WorkerReportsProgress = true;
bw.ProgressChanged += Bw_ProgressChanged;
bw.RunWorkerCompleted += Bw_RunWorkerCompleted;
bw.RunWorkerAsync();
}
private void Bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("attach");
//dt = dl.loadTransfer(status);
//dgvDashboard.AutoGenerateColumns = false;
//dgvDashboard.DataSource = dt;
}
private void Bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (e.ProgressPercentage == 0)
{
label1.Text = "Request Data";
}
else if (e.ProgressPercentage == 3)
{
label1.Text = "Fetching Data";
}
else if (e.ProgressPercentage == 6)
{
label1.Text = "Loading Data";
}
else if (e.ProgressPercentage == 9)
{
label1.Text = "Done";
}
}
private void Bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
int notiCount = 0;
do
{
Thread.Sleep(1000);
notiCount++;
worker.ReportProgress(notiCount);
}
while (notiCount <= 10);
}
}
}
Notice that we're not having to sleep or wait on the UI thread. When progress has been made, we're notified and can access the UI to update it. And when the work is complete, we again get notified.
The only Thread.Sleep we have is within the background worker, and I assume there is standing for actual useful work being done.
You absolutely should not have any Thread.Sleep calls in any constrained context such as the UI thread, where there aren't likely to be any other threads available to service other uses which may want to use your thread whilst you're just blocking it. Even Sleeping a threadpool thread is acting as a bad "team player" in modern multi-threaded applications.
first off I'd like to say I'm brand new to C# so I am not too aware with how the background worker is supposed to be implemented. I have a GUI program that basically pings a domain a returns the response to a textbox. I am able to get it to work normally, however, it freezes the code because it is running on the same thread which is why I am trying to implement a background worker.
Here is the basic setup
private void button1_Click(object sender, EventArgs e)
{
url = textBox1.Text;
button1.Enabled = false;
button2.Enabled = true;
bgWorker.DoWork += new DoWorkEventHandler(bgWorker_DoWork);
bgWorker.RunWorkerAsync();
}
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
do
{
if (bgWorker.CancellationPending)
break;
Invoke((MethodInvoker)delegate { monitor(); });
} while (true);
}
public void monitor()
{
textBox2.AppendText("Status of: " + url + "\n");
Status(url);
System.Threading.Thread.Sleep(30000);
}
private void Status(string url)
{
// This method does all the ping work and also appends the status to the Text box as it goes through , as OK or down
}
I have not worked with bgworkers before and as you can imagine it's confusing. I've looked at tons of other articles and I can't seem to get it. Sorry if the code looks crazy, I'm trying to learn.
Use Microsoft's Reactive Framework (NuGet "System.Reactive.Windows.Forms" and add using System.Reactive.Linq;) and then you can do this:
private void button1_Click(object sender, EventArgs e)
{
var url = textBox1.Text;
Observable
.Interval(TimeSpan.FromMinutes(0.5))
.SelectMany(_ => Observable.Start(() => Status(url)))
.ObserveOn(this)
.Subscribe(status => textBox2.AppendText("Status of: " + status + "\n"));
}
You then just need to change Status to have this signature: string Status(string url).
That's it. No background worker. No invoking. And Status is nicely run on a background thread.
You've got several mistakes. First,
Invoke((MethodInvoker)delegate
{
monitor();
});
will call monitor() on your UI thread. In almost all cases you should not call methods on other threads. You especially should not call methods that block or do anything that takes more than a few milliseconds on your UI thread, and that is what this does:
System.Threading.Thread.Sleep(30000);
Instead of calling a method on another thread; submit immutable data to the other thread and let the thread decide when to handle it. There is an event already built in to BackgroundWorker which does that. Before you call bgWorker.RunWorkerAsync() do this:
url = new Uri(something);
bgWorker.WorkerReportsProgress = true;
bgWorker.WorkerSupportsCancellation = true;
bgWorker.ProgressChanged += Bgw_ProgressChanged;
private void Bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
textBox2.AppendText("Status of: " + url + ": " + e.UserState.ToString()
+ Environment.NewLine);
}
Your bgWorker_DoWork should look more like this:
void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
while (!bgw.CancellationPending)
{
System.Threading.Thread.Sleep(new TimeSpan(0, 0, 30));
var status = ResultOfPing(e.Argument as Uri);
bgw.ReportProgress(0, status);
}
e.Cancel = true;
}
and you should call it like this:
bgWorker.RunWorkerAsync(url);
You've got a second problem. BackgroundWorker creates a thread, and your thread is going to spend most of its time blocked on a timer or waiting for network responses. That is a poor use of a thread. You would be better off using completion callbacks or async/await.
The background worker is running on a thread pool thread, but your call to Status and Sleep is running on the UI thread. You need to move that stuff back into bgWorker_DoWork.
Try this code:
public partial class Form1 : Form
{
bool cancel;
public Form1()
{
InitializeComponent();
}
public void StartPinging()
{
this.cancel = false;
startButton.Enabled = false;
stopButton.Enabled = true;
responseBox.Clear();
responseBox.AppendText("Starting to ping server.");
responseBox.AppendText(Environment.NewLine);
var bw = new BackgroundWorker
{
WorkerReportsProgress = false,
WorkerSupportsCancellation = true
};
bw.DoWork += (obj, ev) =>
{
while (!cancel)
{
// Ping Server Here
string response = Server.PingServer();
this.Invoke(new UiMethod(() =>
{
responseBox.AppendText(response);
responseBox.AppendText(Environment.NewLine);
}));
}
};
bw.RunWorkerCompleted += (obj, ev) =>
{
this.Invoke(new UiMethod(() =>
{
responseBox.AppendText("Stopped pinging the server.");
responseBox.AppendText(Environment.NewLine);
startButton.Enabled = true;
stopButton.Enabled = false;
}));
};
bw.RunWorkerAsync();
}
delegate void UiMethod();
private void startButton_Click(object sender, EventArgs e)
{
StartPinging();
}
private void stopButton_Click(object sender, EventArgs e)
{
responseBox.AppendText("Cancelation Pressed.");
responseBox.AppendText(Environment.NewLine);
cancel = true;
}
}
public class Server
{
static Random rng = new Random();
public static string PingServer()
{
int time = 1200 + rng.Next(2400);
Thread.Sleep(time);
return $"{time} ms";
}
}
Erwin, when dealing with C# - threads and UI elements usually you will come across cross-thread operations i.e. Background thread with UI threads. This interaction needs to be done in thread safe way with the help of Invoke to avoid invalid operations.
Please look into below resource: InvokeRequired section.
https://learn.microsoft.com/en-us/dotnet/framework/winforms/controls/how-to-make-thread-safe-calls-to-windows-forms-controls
I have a problem with background worker, it gets called twice thus, increasing the time of execution for my long routine, I created background worker manually so, there is no chance for the DoWork to be initialized within the initializeComponent() method, any help is appreciated.
here is my code:
// constructor
public TeacherScheduleForm(Therapist therapist)
{
this.therapist = therapist;
InitializeComponent();
bw = new BackgroundWorker();
bw.WorkerSupportsCancellation = true;
bw.WorkerReportsProgress = true;
bw.DoWork += bw_DoWork;
bw.ProgressChanged += bw_ProgressChanged;
bw.RunWorkerCompleted += bw_RunWorkerCompleted;
load = new LoadingForm();
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
load.AppendProgress(e.ProgressPercentage);
// load.AppendText(e.ProgressPercentage.ToString() + "%");
Console.Write("Progress: " + e.ProgressPercentage);
// MessageBox.Show("Progress : " + e.ProgressPercentage);
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if ((e.Cancelled == true))
{
MessageBox.Show("Cancelled");
}
else if (!(e.Error == null))
{
MessageBox.Show("Error : " + e.Error);
}
else
{
updateUI();
load.Close();
Console.Write( "Done!");
}
}
// do work of background worker
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 1; (i <= 2); i++)
{
if ((worker.CancellationPending == true))
{
e.Cancel = true;
break;
}
else
{
// Perform a time consuming operation and report progress.
Console.Write("Before Doing work");
setup(therapist.therapistID + "", schoolYear);// the time consuming operation
Console.Write("Doing work");
//System.Threading.Thread.Sleep(100);
worker.ReportProgress((i*5));
}
}
}
The background worker is called when the user selects the school year through a combo box which is in this code below:
private void comboBoxSchoolYear_SelectedIndexChanged(object sender, EventArgs e)
{
//load = new LoadingForm();
schoolYear = int.Parse(comboBoxSchoolYear.SelectedValue + "");
try{
if (!bw.IsBusy)
{
bw.RunWorkerAsync();
load.ShowDialog();
}
else
{
bw.CancelAsync();
}
}
catch(Exception ex)
{
Console.Write("Error : " + ex.Message);
}
}
You are loading the form after creating the event-handler. Thats the only point I can think off doing the trouble. Try to load the form first and then create the handler.
Reason: At InitializeComponent(); the IndexChanged normally will fire up because the control is set at this point with its index. I havnt noticed this behaviour on FormLoad till now. But as I cant see any other problem in here its worth a try.
IF this doesnt solves it, you should also take care if TeacherScheduleForm is being called twice.
Something handy for debugging-purposes:
MessageBox.Show((new StackTrace().GetFrame(0).GetMethod().Name));
Paste this into your event/method or whatever. It will popup a messagebox with the method-name which called your current method. In this case (from comments) it would've raised 2 messageBoxes saying TeacherScheduleForm for both.
I've saved this to my code-snippets.
I want to display a "loading form" (a form with some text message plus a progressBar with style set to marquee) while the BackgroundWorker's job isn't done. When the BackgroundWorker is done, the loading form must be closed automatically. Although I do use a BackgroundWorker, the main thread should wait until it's done. I was able to do that using a AutoResetEvent but I noticied that as it does block the main thread, the form loading's progressBar is freezed too.
My question is: How can I show that form without freeze it while runing a process in background and wait for it finish? I hope it's clear.
Here's my current code:
BackgroundWorker bw = new BackgroundWorker();
AutoResetEvent resetEvent = new AutoResetEvent(false);
//a windows form with a progressBar and a label
loadingOperation loadingForm = new loadingOperation(statusMsg);
//that form has a progressBar that's freezed. I want to make
// it not freezed.
loadingForm.Show();
bw.DoWork += (sender, e) =>
{
try
{
if (!e.Cancel)
{
//do something
}
}
finally
{
resetEvent.Set();
}
};
bw.RunWorkerAsync();
resetEvent.WaitOne();
loadingForm.Close();
MessageBox.Show("we are done!");
Connect your BackgroundWorker's RunWorkerCompleted to a callback that will close the form like so:
private void backgroundWorker1_RunWorkerCompleted(
object sender, RunWorkerCompletedEventArgs e)
{
loadingForm.Close();
MessageBox.Show("we are done!");
}
You can delete the resetEvent.WaitOne();
You'll need to make loadingForm a field of course.
Tell me more
Occurs when the background operation has completed, has been canceled, or has raised an exception
BackgroundWorker.RunWorkerCompleted
Make the login form object an instance variable.
//use RunWorkerCompleted event to get notified about work completion where you close the form.
bw.RunWorkerCompleted += bw_RunWorkerCompleted;
event handler code:
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.loadingForm.close();
}
Reference: https://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.runworkercompleted(v=vs.110).aspx
Cek this script, Loading is Form with PictureBox - image gif
private delegate void showProgressCallBack(int value);
private void btnStart5_Click(object sender, EventArgs e)
{
BackgroundWorker bw = new BackgroundWorker();
Loading f = new Loading();
f.Show();
bw.DoWork += (s, ea) =>
{
try
{
test1();
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
};
bw.RunWorkerCompleted += (s, ea) =>
{
f.Close();
};
bw.RunWorkerAsync();
}
private void showProgress(int value)
{
if (progressBar1.InvokeRequired)
{
showProgressCallBack showProgressDelegate = new showProgressCallBack(showProgress);
this.Invoke(showProgressDelegate, new object[] {value});
}
else
{
progressBar1.Value = value;
}
}
private void test()
{
showProgress(20);
Thread.Sleep(3000);
showProgress(80);
Thread.Sleep(2000);
showProgress(100);
}
i got some really simple code, but cant get it to work. I'm using BackgroundWorker. Problem is that RunWorkerCompleted is fired way to fast. Instantly after running i get message "Work completed", but application remains frozen for couple of seconds as 'DataType data = new DataType(path);' is beign executed. After that i got all my DataGridViews etc filled correctly. If i swap this single line with Thread.Sleep everything seems to work well. Any ideas?
public frmWindow(string path)
{
InitializeComponent();
DataType d;
backgroundWorker1.RunWorkerAsync(path);
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
string path = e.Argument as string;
DataType data = new DataType(path);
e.Result = data;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
d = e.Result as DataType;
MessageBox.Show("Work completed");
}
How about you use Debug.Write instead of MessageBox.Show with timers to show when the Methods are entered and exited.
While it is possible for this same background thread to act on your UI, its almost always NOT a good thing to do--UI is not threadsafe.
BackgroundWorker backGroundWorker1;
public frmWindow(string path)
{
InitializeComponent();
DataType d;
backGroundWorker1 = new BackgroundWorker();
backGroundWorker1.DoWork += (s, e) =>
{
System.Diagnostics.Debug.Write("Work started at: " + DateTime.Now + Environment.NewLine);
string path = e.Argument as string;
DataType data = new DataType(path);
e.Result = data;
};
backGroundWorker1.RunWorkerCompleted += (s, e) =>
{
d = e.Result as DataType;
System.Diagnostics.Debug.Write("Work completed at: " + DateTime.Now + Environment.NewLine);
};
backGroundWorker1.RunWorkerAsync();
}