C# multi-threading - c#

I have a problem with C# multi-threading.
Form contents are two buttons and two lables.
If I press on the first button, going looping from 1..to 60000, to update label1. ( It works)
If I press on the second button, going looping from 1..to 6000 to update label2,(and my form is lagged). (is not responding)
Please help!
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 ThreadTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(new ThreadStart(countNumbers));
thread.IsBackground = true;
thread.Start();
}
private void button2_Click(object sender, EventArgs e)
{
Thread thread2 = new Thread(new ThreadStart(countNumbers2));
thread2.Start();
}
public void countNumbers()
{
try
{
for (int i = 0; i < 60000; i++)
{
this.Invoke((MethodInvoker)delegate()
{
label2.Text = "" + i.ToString();
}
);
}
}
catch (Exception e)
{
}
}
public void countNumbers2()
{
try
{
for (int i = 0; i < 60000; i++)
{
this.Invoke((MethodInvoker)delegate()
{
label4.Text = "" + i.ToString();
}
);
}
}
catch (Exception e)
{
}
}
private void label3_Click(object sender, EventArgs e)
{
}
}
}

Try using a Forms.Timer in the form and poll a value at regular intervals to update the label in a controlled way. Updating the UI the way you do puts way to much load on the system.
A System.Windows.Forms.Timer runs on the GUI thread.
Just make sure to guard the shared resource in some way, this example uses a volatile member to handle thread synchronization.
You do not need the extra Thread.Sleep(10), it is just there to simulate some load.
private volatile int _counter;
private readonly Timer _timer = new System.Windows.Forms.Timer();
public Form1()
{
InitializeComponent();
_timer.Tick += TimerTick;
_timer.Interval = 20; // ~50 Hz/fps
_timer.Start();
}
void TimerTick(object sender, EventArgs e)
{
_label.Text = _counter.ToString();
}
private void Form1_Load(object sender, EventArgs e)
{
Thread thread = new Thread(CountNumbers) {IsBackground = true};
thread.Start();
}
public void CountNumbers()
{
for (int i = 0; i < 60000; i++)
{
_counter++;
Thread.Sleep(10); // <-- Simulated work load
}
}
Of course, you can easily expand this example to fit your example with two different counters, calculated on separate threads but still using only one Timer to update the entire UI.

You end up with lagging because Invoke (switching to another thread) is very expensive operation and you are calling it too frequently

Try giving this.Refresh() or Application.DoEvents() in your loop

Try to use lock statement
lock (this)
{
label2.Text = "" + i.ToString();
}
you shoud change your code to
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 WindowsFormsApplication23
{
public partial class Form3 : Form
{
public Form3()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(new ThreadStart(countNumbers));
thread.IsBackground = true;
thread.Start();
}
private void button2_Click(object sender, EventArgs e)
{
Thread thread2 = new Thread(new ThreadStart(countNumbers2));
thread2.Start();
}
public void countNumbers()
{
try
{
for (int i = 0; i < 60000; i++)
{
this.Invoke((MethodInvoker)delegate()
{
lock (this)
{
label2.Text = "" + i.ToString();
}
}
);
}
}
catch (Exception e)
{
}
}
public void countNumbers2()
{
try
{
for (int i = 0; i < 60000; i++)
{
this.Invoke((MethodInvoker)delegate()
{
lock (this)
{
label4.Text = "" + i.ToString();
}
}
);
}
}
catch (Exception e)
{
}
}
private void label3_Click(object sender, EventArgs e)
{
}
}
}

Put some synchronization mechanism there
You can use
1.lock(this);
2.monitor.enter(obj); and monitor.exit(obj);
lock (this){
label2.Text = "" + i.ToString();
}
monitor.enter(obj);
label2.Text = "" + i.ToString();
monitor.exit(obj);

Related

How to stop webDriver in backgroundWorker with Stop button with this code?

I now learning backgroundWorker with Selenium and my goal is to integrate webdriver.Close(); and webDriver.Quit() on click button STOP, following code work, but when I try to integrate webDriver.Quit() with STOP button it shows me error, the goal is, when user click STOP, to webdriver close, thanks :)
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;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
namespace WindowsFormsApp19
{
public partial class Form1 : Form
{
private object webDriver;
public Form1()
{
InitializeComponent();
}
public void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
ChromeDriver webDriver = new ChromeDriver();
int sum = 0;
for (int i=1; i<=100; i++)
{
Thread.Sleep(100);
sum = sum + i;
backgroundWorker1.ReportProgress(i);
webDriver.Navigate().GoToUrl("https://www.google.com");
Thread.Sleep(50000);
if (backgroundWorker1.CancellationPending)
{
e.Cancel = true;
backgroundWorker1.ReportProgress(0);
return;
}
}
e.Result = sum;
}
public void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
label1.Text = e.ProgressPercentage.ToString() + "%";
}
public void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if( e.Cancelled)
{
label1.Text = "Operation stopped ";
}
else if (e.Error != null)
{
label1.Text = e.Error.ToString();
}
else
{
label1.Text = "100% Completed";
}
}
public void Startbutton_Click(object sender, EventArgs e)
{
if (!backgroundWorker1.IsBusy)
{
backgroundWorker1.RunWorkerAsync();
}
}
public void Stopbutton_Click(object sender, EventArgs e)
{
if (backgroundWorker1.IsBusy)
{
backgroundWorker1.CancelAsync();
}
}
}
}

c# multiple lock for multiple events

I'm trying to understand the lock mechanism.
if I have multiple events to lock on different value should I use an object lock for each?
More serious code added:
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.IO;
using System.Threading;
namespace ValueChangeOnEventForm
{
public partial class Form1 : Form
{
private Test_Onchange DataSource;
Thread Task1;
private bool Flag_Stop_Task1;
public Form1()
{
InitializeComponent();
graph1.ChartAreas[0].AxisX.ScrollBar.Enabled = true;
graph1.ChartAreas[0].AxisX.IsLabelAutoFit = true;
graph1.ChartAreas[0].AxisX.ScaleView.Size = 100;
graph2.ChartAreas[0].AxisX.ScrollBar.Enabled = true;
graph2.ChartAreas[0].AxisX.IsLabelAutoFit = true;
graph2.ChartAreas[0].AxisX.ScaleView.Size = 100;
DataSource = new Test_Onchange();
DataSource.ValueChanged += new EventHandler(EventValueChange);//Value input info
DataSource.SecondValueChange += new EventHandler(EventSecondValueChange);//second value
Task1 = new Thread(new ThreadStart(Task_1));//create the thread
Task1.Start();//start the thread
}
protected virtual void EventSecondValueChange(object sender, EventArgs e)
{
double valueMAX = 0, size = 0;
if (graph1.InvokeRequired)
{
graph1.Invoke(new MethodInvoker(delegate { graph1.Series["ValueOnGraph"].Points.AddY(DataSource.Value); }));
graph1.Invoke(new MethodInvoker(delegate { valueMAX = graph1.ChartAreas[0].AxisX.Maximum; }));
graph1.Invoke(new MethodInvoker(delegate { size = graph1.ChartAreas[0].AxisX.ScaleView.Size; }));
if (valueMAX - 10 > size)
{
graph1.Invoke(new MethodInvoker(delegate { graph1.ChartAreas[0].AxisX.ScaleView.Scroll(graph1.ChartAreas[0].AxisX.Maximum); }));
graph1.Invoke(new MethodInvoker(delegate { graph1.Series["ValueOnGraph"].Points.RemoveAt(0); }));
}
}
}
protected virtual void EventValueChange(object sender, EventArgs e)
{
double valueMAX=0,size=0;
if (graph2.InvokeRequired)
{
graph2.Invoke(new MethodInvoker(delegate { graph2.Series["ValueOnGraph2"].Points.AddY(DataSource.Secondvalue); }));
graph2.Invoke(new MethodInvoker(delegate { valueMAX = graph2.ChartAreas[0].AxisX.Maximum; }));
graph2.Invoke(new MethodInvoker(delegate { size = graph2.ChartAreas[0].AxisX.ScaleView.Size; }));
if (valueMAX - 10 > size)
{
graph2.Invoke(new MethodInvoker(delegate { graph2.ChartAreas[0].AxisX.ScaleView.Scroll(graph2.ChartAreas[0].AxisX.Maximum); }));
graph2.Invoke(new MethodInvoker(delegate { graph2.Series["ValueOnGraph2"].Points.RemoveAt(0); }));
}
}
}
private void Task_1()
{
while (!Flag_Stop_Task1)
{
Random RandVal = new Random();
Random RandVal2 = new Random();
int Value = RandVal.Next(0, 100);
int SecondValue = RandVal2.Next(50, 200);
DataSource.Value = Value;
DataSource.Secondvalue = SecondValue;
Thread.Sleep(100);
}
Flag_Stop_Task1 = false;
}
private void btn_StopTask_1_Click(object sender, EventArgs e)
{
Flag_Stop_Task1 = true;
}
}
}
And then
namespace ValueChangeOnEventForm
{
class Test_Onchange
{
private int value;
private int secondvalue;
protected object _lock = new object();
public event System.EventHandler ValueChanged;
public event System.EventHandler SecondValueChange;
protected virtual void OnValueChange()
{
lock (this._lock)
{
EventHandler eventvaluechange = ValueChanged;
if (eventvaluechange != null)
eventvaluechange(this, EventArgs.Empty);
}
}
protected virtual void OnSecondValueChange()
{
lock (this._lock)
{
EventHandler eventvaluechange = SecondValueChange;
if (eventvaluechange != null)
eventvaluechange(this, EventArgs.Empty);
}
}
public int Value
{
get { return this.value; }
set
{
if (value != this.value)
{//if value changed enter
this.value = value;
OnValueChange();
}
}
}
public int Secondvalue
{
get { return this.secondvalue; }
set
{
if (value != this.secondvalue)
{//if value changed enter
this.secondvalue = value;
OnSecondValueChange();
}
}
}
}
}
Do I need two lock (lock1 and lock2 object or only one for both value and secondvalue....?
Thanks a lot.
Update
Ok let's do it so.
I'm using beckhoff PLC which are real time Task PLC. and I'm reading two value on it when the value change. like this:
Form1 Class:
namespace RealTimeLock
{
using Beckhoff.App.Ads.Core;
using Beckhoff.App.Ads.Core.Plc;
using TwinCAT.Ads;
using System.IO;
public partial class Form1 : Form
{
private PLC PLCData;
public Form1()
{
InitializeComponent();
}
public Form1(IBAAdsServer _adsServer)
: this()
{
PLCData = new PLC(_adsServer);
PLCData.ErrorBoolChanged += new EventHandler(EventErrorChanged);//error info
PLCData.ForceValChanged += new EventHandler(EventForceChanged);//Force input info
}
protected virtual void EventErrorChanged(object sender, EventArgs e)
{
//state of error PLC
lv_ErrorInfo.Text = "PLC Error num : " + PLCData.i_ErrorID.ToString();
}
protected virtual void EventForceChanged(object sender, EventArgs e)
{//modify graphical data PLC Force data
lv_ForceInfo.Text = PLCData.i_ForceVal.ToString();
c_graphForceIN.Series["ForceData"].Points.AddY(PLCData.i_ForceVal);
if (c_graphForceIN.ChartAreas[0].AxisX.Maximum - 10 > c_graphForceIN.ChartAreas[0].AxisX.ScaleView.Size)
{
c_graphForceIN.ChartAreas[0].AxisX.ScaleView.Scroll(c_graphForceIN.ChartAreas[0].AxisX.Maximum);
c_graphForceIN.Series["ForceData"].Points.RemoveAt(0);
}
}
}
}
Error ID and Force change showed in Form1 label lv_ErrorID and lv_Force and graphForceIN add point.
The events handler on the other side (PLC class) looks like this:
PLC Class:
namespace RealTimeLock
{
using Beckhoff.App.Ads.Core;
using Beckhoff.App.Ads.Core.Plc;
using TwinCAT.Ads;
using System.IO;
public partial class Form1 : Form
{
private PLC PLCData;
public Form1()
{
InitializeComponent();
}
public Form1(IBAAdsServer _adsServer)
: this()
{
PLCData = new PLC(_adsServer);
PLCData.ErrorBoolChanged += new EventHandler(EventErrorChanged);//error info
PLCData.ForceValChanged += new EventHandler(EventForceChanged);//Force input info
}
protected virtual void EventErrorChanged(object sender, EventArgs e)
{
//state of error PLC
lv_ErrorInfo.Text = "PLC Error num : " + PLCData.i_ErrorID.ToString();
}
protected virtual void EventForceChanged(object sender, EventArgs e)
{//modify graphical data PLC Force data
lv_ForceInfo.Text = PLCData.i_ForceVal.ToString();
c_graphForceIN.Series["ForceData"].Points.AddY(PLCData.i_ForceVal);
if (c_graphForceIN.ChartAreas[0].AxisX.Maximum - 10 > c_graphForceIN.ChartAreas[0].AxisX.ScaleView.Size)
{
c_graphForceIN.ChartAreas[0].AxisX.ScaleView.Scroll(c_graphForceIN.ChartAreas[0].AxisX.Maximum);
c_graphForceIN.Series["ForceData"].Points.RemoveAt(0);
}
}
}
}
Does it seem to be correct coding for you guys? and while I have a real time task running there do I need to lock variables and if so, do I need two lock or only one??
Thanks for your remark on this!!

Backgroundworker lag when starting, split method for multiple threads

The below code is for searching files. The thing is that when I select a large folder (like C disk) program starts with some delay. I think I need to divide disk into smaller parts (folders) and run it on different threads, but I do not know how.
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.Tasks;
using System.Threading;
using System.IO;
using System.Collections;
using System.Diagnostics;
namespace failu_paieska
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;
}
FolderBrowserDialog Folder = new FolderBrowserDialog();
Stopwatch stopwatch = new Stopwatch();
static List<string> lstFilesFound = new List<string>();
int fileCount;
int fileCount1;
//string test;
private void Form1_Load(object sender, EventArgs e)
{
//label2.Text = Environment.ProcessorCount.ToString();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
}
private void label1_Click(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
DialogResult result = Folder.ShowDialog();
textBox2.Text = Folder.SelectedPath.ToString();
}
private void IdetiIListbox()
{
foreach (string n in lstFilesFound.ToList())
{
stopwatch.Start();
if (Path.GetFileName(n).Contains(textBox1.Text.ToString()))
{
this.BeginInvoke(new MethodInvoker(() =>
{
listBox1.Items.Add(n);
//progressBar1.Value++;
}));
}
}
stopwatch.Stop();
int paieska = listBox1.Items.Count;
this.BeginInvoke(new MethodInvoker(() =>
{
textBox3.Text = Convert.ToString(stopwatch.Elapsed);
}));
}
public void Ieskoti1()
{
foreach (string ff in Directory.EnumerateFiles(textBox2.Text.ToString(), "*.*"))
{// Paima failus is pirmo katalogo
lstFilesFound.Add(ff);
fileCount1 = lstFilesFound.Count();
}
}
private void button2_Click(object sender, EventArgs e)
{
if (backgroundWorker1.IsBusy != true)
{
backgroundWorker1.RunWorkerAsync(textBox2.Text.ToString());
}
}
static void DirSearch(string sDir)
{
foreach (string d in Directory.EnumerateDirectories(sDir))
{
try
{
lstFilesFound.Add(d);
//visoFailu += 1;
foreach (string f in Directory.EnumerateFiles(d, "*.*"))
{
//visoFailu += 1;
lstFilesFound.Add(f);
}
DirSearch(d);
}
catch (Exception ee)
{
}
continue;
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label4.Text = (e.ProgressPercentage.ToString() + "%");
progressBar1.Value = e.ProgressPercentage;
}
private void backgroundWorker1_DoWork_1(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
//DirSearch(textBox2.Text.ToString());
Ieskoti1();
DirSearch(textBox2.Text.ToString());
// IdetiIListbox();
for (int i = 0; i <= 100; i++)
{
backgroundWorker1.ReportProgress(i);
System.Threading.Thread.Sleep(100);
}
}
private void backgroundWorker1_RunWorkerCompleted_1(object sender, RunWorkerCompletedEventArgs e)
{
//DirSearch(textBox2.Text.ToString());
IdetiIListbox();
if (e.Cancelled == true)
{
MessageBox.Show("Cancelled!");
}
else if (e.Error != null)
{
MessageBox.Show("Error: " + e.Error.Message);
}
else
{
MessageBox.Show("Done!");
}
}
private void textBox2_TextChanged(object sender, EventArgs e)
{
fileCount = Directory.GetFiles(textBox2.Text.ToString()).Length;
}
}
}

C# Background Worker DoWork getting stuck

Hey I have this test back ground worker which seems to get stuck on the DoWork method or maybe the RunWorkerCompleted is not being fired can you guys see anything wrong here?
Maybe I am not implementing this properly :/
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 MailChimp;
using System.Threading;
using System.Runtime.InteropServices;
namespace Chimporter
{
public partial class Form1 : Form
{
//Worker thread flag set to false
static bool done = false;
//Console dll
[DllImport("Kernel32.dll")]
static extern Boolean AllocConsole();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
}
private void label1_Click(object sender, EventArgs e)
{
}
private void accountInformationToolStripMenuItem_Click(object sender, EventArgs e)
{
//loadWindow pleaseWait = new loadWindow();
//pleaseWait.Show();
BackgroundWorker bg = new BackgroundWorker();
bg.DoWork += new DoWorkEventHandler(bg_DoWork);
bg.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bg_RunWorkerCompleted);
bg.RunWorkerAsync();
while (!done)
{
//Console.WriteLine("Waiting in Main, tid " + Thread.CurrentThread.ManagedThreadId);
//Thread.Sleep(100);
}
//AccountInfo accInfo = new AccountInfo();
//accInfo.Show();
}
public void bg_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (!AllocConsole())
{
Console.WriteLine("Fnished! " + Thread.CurrentThread.ManagedThreadId);
}
done = true;
}
public void bg_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i <= 3; i++)
{
if (!AllocConsole())
{
Console.WriteLine("Work Line: " + i + ", tid " + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(500);
}
}
//string key = "e42713458882f6c2c27b3d6d951174a2-us6";
//var mc = new MCApi(key, true);
//string user = mc.GetAccountDetails().Username.ToString();
return;
}
private void menuStrip1_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
{
}
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
//Exit application Form.Close()
Application.Exit();
}
}
}
Take your while (!done) out. It is locking up the main thread. The worker completed event gets raised on that thread, but since it is busy in a loop it will never get raised.
The whole point of the RunWorkerCompleted event is so that you get a notification on the main thread and you don't have to lock it up in a busy loop and make your gui unresponsive.

backgroundworker class in WPF

I am learning backgroundworker class in WPF. The code below is in file MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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.ComponentModel;
namespace FrontEnd
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private BackgroundWorker backGroundWorker;
public MainWindow()
{
InitializeComponent();
backGroundWorker = ((BackgroundWorker)this.FindResource("backgroundWorker"));
}
private void button1_Click(object sender, RoutedEventArgs e)
{
button1.IsEnabled = false;
Flow pro = new Flow(20,10);
backGroundWorker.RunWorkerAsync(pro);
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
Flow pro = (Flow)e.Argument;
e.Result = pro.NaturalNumbers();
}
private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value= e.ProgressPercentage;
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if ((int)e.Result == 1) MessageBox.Show("DONE");
progressBar1.Value = 0;
}
}
}
The code below is in file Flow.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
namespace FrontEnd
{
class Flow
{
long i;
//private int x,y;
public int X
{
get; set;
}
public int Y
{
get; set;
}
public Flow(int x, int y)
{
X = x;
Y = y;
}
public int NaturalNumbers()
{
for (i = 0; i < 9999; i++)
{
Console.WriteLine(i);
long iteration = i * 100 / 9999;
if ((i % iteration == 0) &&
(backgroundWorker != null) && backgroundWorker.WorkerReportsProgress)
{
backgroundWorker.ReportProgress(iteration);
}
}
return 1;
}
}
}
Error : The name 'backgroundWorker' does not exist in the current
context
How can I make progress bar working?
Here's a simple example that works:
public partial class BackgroundWorkerPage : Page
{
private readonly BackgroundWorker _worker = new BackgroundWorker();
public BackgroundWorkerPage()
{
InitializeComponent();
_worker.DoWork += WorkerOnDoWork;
_worker.WorkerReportsProgress = true;
_worker.ProgressChanged += WorkerOnProgressChanged;
}
private void WorkerOnProgressChanged(object sender, ProgressChangedEventArgs progressChangedEventArgs)
{
progressBar.Value = progressChangedEventArgs.ProgressPercentage;
}
private void WorkerOnDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
{
for (int i = 0; i <= 100; i++)
{
Thread.Sleep(50);
_worker.ReportProgress(i);
}
}
private void Button_Click_1(object sender, System.Windows.RoutedEventArgs e)
{
_worker.RunWorkerAsync();
}
}
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<ProgressBar x:Name="progressBar" Height="23" Minimum="0" Maximum="100"/>
<Button Grid.Row="1" Height="23" Content="Start" Click="Button_Click_1"/>
</Grid>
And you need to change your code a bit
private void WorkerOnDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
{
var flow = new Flow(_worker);
flow.NaturalNumbers();
}
internal class Flow
{
private readonly BackgroundWorker _worker;
public Flow(int x, int y)
{
X = x;
Y = y;
}
public Flow(BackgroundWorker worker)
{
_worker = worker;
}
public int X { get; set; }
public int Y { get; set; }
public int NaturalNumbers()
{
for (int i = 0; i <= 9999; i++)
{
int iteration = i*100/9999;
// your if(...) fails with divide by zero exception
_worker.ReportProgress(iteration);
}
return 1;
}
}
Introduction
Whenever we try to do some long running operation on the UI without freezing it, we need to run it in a separate thread.In this article we will look into the BackgroundWorker class , as one of the various solutions to this problem with a simple example. BackgroundWorker executes an operation on a separate thread and provide a notification to UI Thread whenever necessary.
Straight To Experiment
Let us create a UI as under
The objective is that,when we will click on the "Populate" button, at the same time we should be able to write something in the "Textbox".
Now let us look into the code which is without BackgroundWorker
public partial class WithOutBackgroundThread : Form
{
List<Employee> lstEmp;
public WithOutBackgroundThread()
{
InitializeComponent();
lstEmp = new List<Employee>();
}
private void btnPopulate_Click(object sender, EventArgs e)
{
GetEmployeeRecords();
dataGridView1.DataSource = lstEmp;
lblStatus.Text = "Work Completed";
}
//Prepare the data
private void GetEmployeeRecords()
{
for (int i = 1; i <= 10; i++)
{
// Simulate a pause
Thread.Sleep(1000);
lstEmp.Add(new Employee { EmpId = i, EmpName = "Name" + i });
}
}
}
The code is pretty straightforward.In the "GetEmployeeRecords()" method, we are preparing the data.We have introduce the "Thread.Sleep(1000)" to make a delay. And in the "Populate" button click event, we are populating the Gird.
But if we execute this code, the UI will become unresponsive and henceforth, we cannot perform any task on the "Textbox" which is our objective.
Henceforth let us change our code to the below
public partial class WithBackgroundThread : Form
{
BackgroundWorker workerThread;
List<Employee> lstEmp;
public WithBackgroundThread()
{
InitializeComponent();
lstEmp = new List<Employee>();
workerThread = new BackgroundWorker();
workerThread.DoWork += new DoWorkEventHandler(workerThread_DoWork);
workerThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(workerThread_RunWorkerCompleted);
}
private void btnPopulate_Click(object sender, EventArgs e)
{
workerThread.RunWorkerAsync();
}
private void workerThread_DoWork(object sender, DoWorkEventArgs e)
{
GetEmployeeRecords();
}
private void workerThread_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
lblStatus.Text = "Work Completed";
dataGridView1.DataSource = lstEmp;
}
//Prepare the data
private void GetEmployeeRecords()
{
for (int i = 1; i <= 10; i++)
{
// Simulate a pause
Thread.Sleep(1000);
lstEmp.Add(new Employee { EmpId = i, EmpName = "Name" + i });
}
}
}
A lot of new things.We will explore one by one.
First, we need to declare BackgroundWorker Thread
BackgroundWorker workerThread = new BackgroundWorker();
Next,we need to subscribe to the events
workerThread.DoWork += new DoWorkEventHandler(workerThread_DoWork);
workerThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(workerThread_RunWorkerCompleted);
As a third step, we need to implement the two methods
private void workerThread_DoWork(object sender, DoWorkEventArgs e)
{
// run all background tasks here
GetEmployeeRecords();
}
private void workerThread_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//update ui once worker complete its work
lblStatus.Text = "Work Completed";
dataGridView1.DataSource = lstEmp;
}
The "DoWork" event is raised when we call the "RunWorkerAsync" method. This is where we start the operation that performs the potentially time-consuming work.The "RunWorkerCompleted" event is fired when the background operation has completed, has been canceled, or has raised an exception
As a last step, invoke the "RunWorkerAsync" from the "Populate" button click event.
private void btnPopulate_Click(object sender, EventArgs e)
{
workerThread.RunWorkerAsync();
}
The "RunWorkerAsync" starts execution of a background operation.
Now if we run our application we will be able to populate the grid as well as write something on the "Textbox".
Thanks

Categories

Resources