Run backgroundtask simultaneous with foreground xaml - c#

I am trying to integrate a background application into a foreground application which needs to run simultaneously.
I have been reading many Microsoft docs and it seems like I cant understand what they meant. I am a begineer at XAML.This is supposed to be for raspberry pi where the background codes are for the sensors while the foreground should have buttons where user can tap for an OTP if they dont have RFID.The OTP i can manage but i am having problem integrating background into XAML.
I am overly stressed about this. Please help me
UPDATE:
I am going to try your suggestion on the Pi tomorrow and hopefully it will work :)
private void OnPageLoaded(object sender, RoutedEventArgs e)
{
int interval = 20;
DateTime dueTime = DateTime.Now.AddMilliseconds(interval);
while (true)
{
if (DateTime.Now >= dueTime)
{
//insert code here
initComms();
StartUart();
//self monitoring
startLightMonitoring();
//Initial State
CurMode = Neutral;
Debug.WriteLine("===Entering MODE_SENDLIGHT===");
// This make sure the main program run idefinitely
while (true)
{
Sleep(300);
//state machine
handleModeSendLight();
if (CurMode == Neutral)
{
MailBoxMonitoring();
}
else if (CurMode == AccessMode)
{
AccessGranted();
}
else if (CurMode == IntrusionMode)
{
InvalidAccess();
}
else
{
Debug.WriteLine("Config Error");
}
//Update next dueTime
dueTime = DateTime.Now.AddMilliseconds(interval);
}
}
else
{
//Just yield to not tax out the CPU
Sleep(1);
}
}
}

MVVM:
You are using XAML and UWP. That means you should be using the MVVM pattern. While you can use other approaches, XAML and WPF/UWP were designed with MVVM in mind. You loose about 90% of the power of XAML and run into loads of additonal issues by not using MVVM. I wrote a intro into it years back:
https://social.msdn.microsoft.com/Forums/vstudio/en-US/b1a8bf14-4acd-4d77-9df8-bdb95b02dbe2/lets-talk-about-mvvm?forum=wpf
Background Operations:
There are many ways to do Multitasking/-Threading in .NET. BackgroundWorker, Async...await, Tasks, Threads an what you linked in the comments above. For beginners I would advise the background worker and I wrote a example code for this. But your case is not precisely beginner level:
#region Primenumbers
private void btnPrimStart_Click(object sender, EventArgs e)
{
if (!bgwPrim.IsBusy)
{
//Prepare ProgressBar and Textbox
int temp = (int)nudPrim.Value;
pgbPrim.Maximum = temp;
tbPrim.Text = "";
//Start processing
bgwPrim.RunWorkerAsync(temp);
}
}
private void btnPrimCancel_Click(object sender, EventArgs e)
{
if (bgwPrim.IsBusy)
{
bgwPrim.CancelAsync();
}
}
private void bgwPrim_DoWork(object sender, DoWorkEventArgs e)
{
int highestToCheck = (int)e.Argument;
//Get a reference to the BackgroundWorker running this code
//for Progress Updates and Cancelation checking
BackgroundWorker thisWorker = (BackgroundWorker)sender;
//Create the list that stores the results and is returned by DoWork
List<int> Primes = new List<int>();
//Check all uneven numbers between 1 and whatever the user choose as upper limit
for(int PrimeCandidate=1; PrimeCandidate < highestToCheck; PrimeCandidate+=2)
{
//Report progress
thisWorker.ReportProgress(PrimeCandidate);
bool isNoPrime = false;
//Check if the Cancelation was requested during the last loop
if (thisWorker.CancellationPending)
{
//Tell the Backgroundworker you are canceling and exit the for-loop
e.Cancel = true;
break;
}
//Determin if this is a Prime Number
for (int j = 3; j < PrimeCandidate && !isNoPrime; j += 2)
{
if (PrimeCandidate % j == 0)
isNoPrime = true;
}
if (!isNoPrime)
Primes.Add(PrimeCandidate);
}
//Tell the progress bar you are finished
thisWorker.ReportProgress(highestToCheck);
//Save Return Value
e.Result = Primes.ToArray();
}
private void bgwPrim_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
pgbPrim.Value = e.ProgressPercentage;
}
private void bgwPrim_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
pgbPrim.Value = pgbPrim.Maximum;
this.Refresh();
if (!e.Cancelled && e.Error == null)
{
//Show the Result
int[] Primes = (int[])e.Result;
StringBuilder sbOutput = new StringBuilder();
foreach (int Prim in Primes)
{
sbOutput.Append(Prim.ToString() + Environment.NewLine);
}
tbPrim.Text = sbOutput.ToString();
}
else
{
tbPrim.Text = "Operation canceled by user or Exception";
}
}
#endregion
You could use progress report to deliver the current sensor data out of the Thread. Expose them in a Property on the ViewModel.
GUI Write overhead:
Your case is tricky, because you need a thread that pretty much runs permanently and does most work via Progress Reporting. The thing is that writing the GUI is quite costly. That does not mater if you only do it once per user started event, but here you have a loop (in the other Thread or Task) doing repeated writing (via change notification or directly). You can totally overload the GUI thread with too many changes. Indeed my original variant of above code did just that: I tried to hand out each found prime via Progress reporting and then append it to the output box. At a large enough upper bound it found so many primes so quickly, that string appending them to the output box fully taxed out the GUI thread for a while. As primes became rare, the GUI thread managed to catch up. But you data gathering will always work on the same speed. I wrote an example for this issues too:
using System;
using System.Windows.Forms;
namespace UIWriteOverhead
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
int[] getNumbers(int upperLimit)
{
int[] ReturnValue = new int[upperLimit];
for (int i = 0; i < ReturnValue.Length; i++)
ReturnValue[i] = i;
return ReturnValue;
}
void printWithBuffer(int[] Values)
{
textBox1.Text = "";
string buffer = "";
foreach (int Number in Values)
buffer += Number.ToString() + Environment.NewLine;
textBox1.Text = buffer;
}
void printDirectly(int[] Values){
textBox1.Text = "";
foreach (int Number in Values)
textBox1.Text += Number.ToString() + Environment.NewLine;
}
private void btnPrintBuffer_Click(object sender, EventArgs e)
{
MessageBox.Show("Generating Numbers");
int[] temp = getNumbers(10000);
MessageBox.Show("Printing with buffer");
printWithBuffer(temp);
MessageBox.Show("Printing done");
}
private void btnPrintDirect_Click(object sender, EventArgs e)
{
MessageBox.Show("Generating Numbers");
int[] temp = getNumbers(1000);
MessageBox.Show("Printing directly");
printDirectly(temp);
MessageBox.Show("Printing done");
}
}
}
There are two ways around that:
Do not use change notification. You can still use a property that is equiped for it on the ViewModel side, but do not let the View Register it. Instead let the view poll the values via a Timer. That way you can keep the changes of the GUI to a manageable level.
Limit how often new data is gathered. You can apply simple rate limiting so new data is not aquired (and Reported) too often:
integer interval = 20;
DateTime dueTime = DateTime.Now.AddMillisconds(interval);
while(true){
if(DateTime.Now >= dueTime){
//insert code here
//Update next dueTime
dueTime = DateTime.Now.AddMillisconds(interval);
}
else{
//Just yield to not tax out the CPU
Thread.Sleep(1);
}
}
Note that this is only a upper bound. If data gathering takes 20 milliseconds each itteartion, this code will not run faster. Also the Windows Clock used by DateTime.Now is not nearly as accurate as the Type allows Precision. In tests I had delays of 5-18 ms and those values kept changing.

Related

How to prevent program from not responding while a loading bar is present

I am creating a program that can read lots of data from a file when it starts up.
I tried a progress bar that will display the progress of the load hoping that it will stop the not responding but instead it froze halfway through and updating a label on the screen to fix the problem.
//This will be 'private void Form1_Shown(object sender, EventArgs e)'
private void loadingBarToolStripMenuItem_Click(object sender, EventArgs e)
{
progressBar1.Show();
for (int i = 0; i < 100; i++)
{
progressBar1.Value = i;
System.Threading.Thread.Sleep(50);
//StartRespondingAgain();
}
progressBar1.Hide();
}
This issue is not the loading bar, but that you are running a long lasting operation on the GUI Thread.
Events are suppossed to be called, finish their work and return ASAP. Only one piece of code can run at the same time, and while this event runs, no other event - indeed not even the drawing of the changes - can be executed.
You need to add some form of Multitasking. This operation will not benefit from a lot of Threads/tasks (like one per file), but at least the long running loop can be moved into a seperate task. BackgroundWorkers, Threads and Async/Await are just 3 approaches. I personally consider the BackgroundWorker to be good - but slightly dated - "training Wheels" to learning Multitasking and in particular Multithreading. I even got some example code for it:
#region Primenumbers
private void btnPrimStart_Click(object sender, EventArgs e)
{
if (!bgwPrim.IsBusy)
{
//Prepare ProgressBar and Textbox
int temp = (int)nudPrim.Value;
pgbPrim.Maximum = temp;
tbPrim.Text = "";
//Start processing
bgwPrim.RunWorkerAsync(temp);
}
}
private void btnPrimCancel_Click(object sender, EventArgs e)
{
if (bgwPrim.IsBusy)
{
bgwPrim.CancelAsync();
}
}
private void bgwPrim_DoWork(object sender, DoWorkEventArgs e)
{
int highestToCheck = (int)e.Argument;
//Get a reference to the BackgroundWorker running this code
//for Progress Updates and Cancelation checking
BackgroundWorker thisWorker = (BackgroundWorker)sender;
//Create the list that stores the results and is returned by DoWork
List<int> Primes = new List<int>();
//Check all uneven numbers between 1 and whatever the user choose as upper limit
for(int PrimeCandidate=1; PrimeCandidate < highestToCheck; PrimeCandidate+=2)
{
//Report progress
thisWorker.ReportProgress(PrimeCandidate);
bool isNoPrime = false;
//Check if the Cancelation was requested during the last loop
if (thisWorker.CancellationPending)
{
//Tell the Backgroundworker you are canceling and exit the for-loop
e.Cancel = true;
break;
}
//Determin if this is a Prime Number
for (int j = 3; j < PrimeCandidate && !isNoPrime; j += 2)
{
if (PrimeCandidate % j == 0)
isNoPrime = true;
}
if (!isNoPrime)
Primes.Add(PrimeCandidate);
}
//Tell the progress bar you are finished
thisWorker.ReportProgress(highestToCheck);
//Save Return Value
e.Result = Primes.ToArray();
}
private void bgwPrim_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
pgbPrim.Value = e.ProgressPercentage;
}
private void bgwPrim_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
pgbPrim.Value = pgbPrim.Maximum;
this.Refresh();
if (!e.Cancelled && e.Error == null)
{
//Show the Result
int[] Primes = (int[])e.Result;
StringBuilder sbOutput = new StringBuilder();
foreach (int Prim in Primes)
{
sbOutput.Append(Prim.ToString() + Environment.NewLine);
}
tbPrim.Text = sbOutput.ToString();
}
else
{
tbPrim.Text = "Operation canceled by user or Exception";
}
}
#endregion
But that is an area where you can pick your poison.

Updating progressbar through backgroundworker in method

My programm (in C# using Windows Forms) is reading and parsing large amounts of Data and I'm using a Backgroundworker which calls those global methods (reading and parsing). I'd like to keep the user updated on how long it's going to take, so the Backgroundworker is supposed to display what action its doing and has a progressbar that should fill for every individual action too.
Unfortunately, I can't get it to work, as the progressbar just doesn't update at all and just stays empty.
Here is what I have so far:
private void InitializeBackgroundWorker()
{
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
}
private void buttonParse_Click(object sender, EventArgs e)
{
DescriptionLabel.Visible = true;
progressBar1.Visible = true;
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
Methods.ParsePerfusionData(backgroundWorker1); //Also tried using 'worker' here, but didnt work either
}
And in the method it looks like that:
public static void ParsePerfusionData(BackgroundWorker worker)
{
for (int i = 2; i < Globals.DataList.Count; i++)
{
worker.ReportProgress(i / amount * 100);
rest of the code etc.
}
}
Can I not use a backgroundworker in a global method like that? Thanks in advance!
When i < amount then i / amount * 100 = 0 * 100 = 0.
Simply use i * 100 / amount instead.
Also make sure backgroundWorker1.WorkerReportsProgress = true
You can only report progress between distinct operations. That means either:
using a very modern class that supports this level of reporting. Such a classs might not exist for your case.
reverse engineering parts of the code down to the loop you want to make reporting on. Usually the loop that itterates over files or the like.
GUI updates must be contained to RunWorkerCompelted and ProgressReport events. And depending on how often updates happen, ProgressReport may have to be kept to only updating a progress bar.
Here some old code I wrote with BackgroundWorker wich should get you started:
#region Primenumbers
private void btnPrimStart_Click(object sender, EventArgs e)
{
if (!bgwPrim.IsBusy)
{
//Prepare ProgressBar and Textbox
int temp = (int)nudPrim.Value;
pgbPrim.Maximum = temp;
tbPrim.Text = "";
//Start processing
bgwPrim.RunWorkerAsync(temp);
}
}
private void btnPrimCancel_Click(object sender, EventArgs e)
{
if (bgwPrim.IsBusy)
{
bgwPrim.CancelAsync();
}
}
private void bgwPrim_DoWork(object sender, DoWorkEventArgs e)
{
int highestToCheck = (int)e.Argument;
//Get a reference to the BackgroundWorker running this code
//for Progress Updates and Cancelation checking
BackgroundWorker thisWorker = (BackgroundWorker)sender;
//Create the list that stores the results and is returned by DoWork
List<int> Primes = new List<int>();
//Check all uneven numbers between 1 and whatever the user choose as upper limit
for(int PrimeCandidate=1; PrimeCandidate < highestToCheck; PrimeCandidate+=2)
{
//Report progress
thisWorker.ReportProgress(PrimeCandidate);
bool isNoPrime = false;
//Check if the Cancelation was requested during the last loop
if (thisWorker.CancellationPending)
{
//Tell the Backgroundworker you are canceling and exit the for-loop
e.Cancel = true;
break;
}
//Determin if this is a Prime Number
for (int j = 3; j < PrimeCandidate && !isNoPrime; j += 2)
{
if (PrimeCandidate % j == 0)
isNoPrime = true;
}
if (!isNoPrime)
Primes.Add(PrimeCandidate);
}
//Tell the progress bar you are finished
thisWorker.ReportProgress(highestToCheck);
//Save Return Value
e.Result = Primes.ToArray();
}
private void bgwPrim_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
pgbPrim.Value = e.ProgressPercentage;
}
private void bgwPrim_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
pgbPrim.Value = pgbPrim.Maximum;
this.Refresh();
if (!e.Cancelled && e.Error == null)
{
//Show the Result
int[] Primes = (int[])e.Result;
StringBuilder sbOutput = new StringBuilder();
foreach (int Prim in Primes)
{
sbOutput.Append(Prim.ToString() + Environment.NewLine);
}
tbPrim.Text = sbOutput.ToString();
}
else
{
tbPrim.Text = "Operation canceled by user or Exception";
}
}
#endregion

How can I make it work this "responsive Progressbar"?

Good morning, I'm trying to write an application that use in his interface a progressbar (in C#, WPF). I have read about the need of perform the UI task in a different thread, using Backgroundworker. I trying to make it work using a lot of information, but nothing happens (the program work fine, but the progressbar only shown at the end of the "hard-work tasks").
I'm civil engineer (not a software one), so I ask if anyone can help me with that.
namespace SAP2000___Quake_Definitions
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private readonly BackgroundWorker bgWoker = new BackgroundWorker();
public MainWindow()
{
InitializeComponent();
this.bgWoker.WorkerReportsProgress = true;
this.bgWoker.WorkerSupportsCancellation = true;
this.bgWoker.DoWork += bgWorker_DoWork;
this.bgWoker.ProgressChanged += bgWorker_ProgressChanged;
this.bgWoker.RunWorkerCompleted += bgWorker_RunWorkerCompleted;
}
private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.progBar.Value = e.ProgressPercentage;
}
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bgWorker = (BackgroundWorker)sender;
Dispatcher.Invoke(new Action(() => DoTheHardWork()));
}
private void processButton_Click(object sender, RoutedEventArgs e)
{
this.bgWoker.RunWorkerAsync();
}
private void DoTheHardWork()
{
switch (this.chckBox2.IsChecked.GetValueOrDefault())
{
case true:
this.bgWoker.ReportProgress(0);
//more hardwork with inputs from WPF
case false:
this.bgWoker.ReportProgress(0);
//more hardwork with inputs from WPF
}
}
}
}
That is not how you should be using a BackgroundWorker. I wrote some example code a few years back. It should get you on the right track:
#region Primenumbers
private void btnPrimStart_Click(object sender, EventArgs e)
{
if (!bgwPrim.IsBusy)
{
//Prepare ProgressBar and Textbox
int temp = (int)nudPrim.Value;
pgbPrim.Maximum = temp;
tbPrim.Text = "";
//Start processing
bgwPrim.RunWorkerAsync(temp);
}
}
private void btnPrimCancel_Click(object sender, EventArgs e)
{
if (bgwPrim.IsBusy)
{
bgwPrim.CancelAsync();
}
}
private void bgwPrim_DoWork(object sender, DoWorkEventArgs e)
{
int highestToCheck = (int)e.Argument;
//Get a reference to the BackgroundWorker running this code
//for Progress Updates and Cancelation checking
BackgroundWorker thisWorker = (BackgroundWorker)sender;
//Create the list that stores the results and is returned by DoWork
List<int> Primes = new List<int>();
//Check all uneven numbers between 1 and whatever the user choose as upper limit
for(int PrimeCandidate=1; PrimeCandidate < highestToCheck; PrimeCandidate+=2)
{
//Report progress
thisWorker.ReportProgress(PrimeCandidate);
bool isNoPrime = false;
//Check if the Cancelation was requested during the last loop
if (thisWorker.CancellationPending)
{
//Tell the Backgroundworker you are canceling and exit the for-loop
e.Cancel = true;
break;
}
//Determin if this is a Prime Number
for (int j = 3; j < PrimeCandidate && !isNoPrime; j += 2)
{
if (PrimeCandidate % j == 0)
isNoPrime = true;
}
if (!isNoPrime)
Primes.Add(PrimeCandidate);
}
//Tell the progress bar you are finished
thisWorker.ReportProgress(highestToCheck);
//Save Return Value
e.Result = Primes.ToArray();
}
private void bgwPrim_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
pgbPrim.Value = e.ProgressPercentage;
}
private void bgwPrim_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
pgbPrim.Value = pgbPrim.Maximum;
this.Refresh();
if (!e.Cancelled && e.Error == null)
{
//Show the Result
int[] Primes = (int[])e.Result;
StringBuilder sbOutput = new StringBuilder();
foreach (int Prim in Primes)
{
sbOutput.Append(Prim.ToString() + Environment.NewLine);
}
tbPrim.Text = sbOutput.ToString();
}
else
{
tbPrim.Text = "Operation canceled by user or Exception";
}
}
#endregion
You have to limit all UI writing work to the Progress Report and Run wokrer compelte Events. Those will be raised in the thread that created the BGW (wich should be the UI thread) automagically.
Note that you can only report progress between distinct steps. I had the advantage that I had to write the loop anyway. But if you have existing code (like most download or disk code), you can usually only report between files.
my mistakes were three:
Trying to use "Dispatcher.Invoke(new Action(() => DoTheHardWork()));" to solve an exception related to my thread (exception caused by point #3).
Avoiding the instantiation: BackgroundWorker bgWorker = (BackgroundWorker)sender (thank you #Christopher).
Writing a code that manipulate a UI-Component inside the DoWork event handle of my Backgroundworker. MSDN says: You must be careful not to manipulate any user-interface objects in your DoWork event handler. Instead, communicate to the user interface through the ProgressChanged and RunWorkerCompleted events. Trying this, the exception occur.
Solving the point #2 and #3, the UI is perfectly responsive respect to the "hardwork" function (runned in background).

C#.net, raising events while processing a long task, show a form which can have "Pause" and "Continue" to continue processing

I process a file line by line reading various events that have a time stamp and data associated with them. I want to be able to show a form while doing processing which I need to interact with and intercept some events by having a button saying interrupt EventX and if it is pressed it will show the event data in a rich text box field when this event is reached sometime in the future. I can then change some of that event data (let's say I simulate some conditions) and when I press "Resume" it should resume processing by raising an event to the intended subscriber for further processing.
So I need an interceptor that will be pass-trough mechanism when a certain form element is pressed and pass that data to the intended subscriber.
I am ok to wait synchronously for modifying data and pressing "Resume"
Thanks
If you want to have a Responsive GUI while doing a long running operation, you need some form of Multitasking. Wich means either async/await or any of the many Multithreading (Thread and BackgroundWorker, mostly) approaches
While pause and resume could be added, doing so usually more work then it is worth. At the very least you run into issues like still held filehandles or race conditions. Often a "cancel" action is way enough/better then a full stop/resume mechanic.
As a Beginner I would advice you to use the BackgroundWorker. It is about as easy as getting into Multitasking via Multithreading can be. I even wrote a example for it a few years back:
#region Primenumbers
private void btnPrimStart_Click(object sender, EventArgs e)
{
if (!bgwPrim.IsBusy)
{
//Prepare ProgressBar and Textbox
int temp = (int)nudPrim.Value;
pgbPrim.Maximum = temp;
tbPrim.Text = "";
//Start processing
bgwPrim.RunWorkerAsync(temp);
}
}
private void btnPrimCancel_Click(object sender, EventArgs e)
{
if (bgwPrim.IsBusy)
{
bgwPrim.CancelAsync();
}
}
private void bgwPrim_DoWork(object sender, DoWorkEventArgs e)
{
int highestToCheck = (int)e.Argument;
//Get a reference to the BackgroundWorker running this code
//for Progress Updates and Cancelation checking
BackgroundWorker thisWorker = (BackgroundWorker)sender;
//Create the list that stores the results and is returned by DoWork
List<int> Primes = new List<int>();
//Check all uneven numbers between 1 and whatever the user choose as upper limit
for(int PrimeCandidate=1; PrimeCandidate < highestToCheck; PrimeCandidate+=2)
{
//Report progress
thisWorker.ReportProgress(PrimeCandidate);
bool isNoPrime = false;
//Check if the Cancelation was requested during the last loop
if (thisWorker.CancellationPending)
{
//Tell the Backgroundworker you are canceling and exit the for-loop
e.Cancel = true;
break;
}
//Determin if this is a Prime Number
for (int j = 3; j < PrimeCandidate && !isNoPrime; j += 2)
{
if (PrimeCandidate % j == 0)
isNoPrime = true;
}
if (!isNoPrime)
Primes.Add(PrimeCandidate);
}
//Tell the progress bar you are finished
thisWorker.ReportProgress(highestToCheck);
//Save Return Value
e.Result = Primes.ToArray();
}
private void bgwPrim_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
pgbPrim.Value = e.ProgressPercentage;
}
private void bgwPrim_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
pgbPrim.Value = pgbPrim.Maximum;
this.Refresh();
if (!e.Cancelled && e.Error == null)
{
//Show the Result
int[] Primes = (int[])e.Result;
StringBuilder sbOutput = new StringBuilder();
foreach (int Prim in Primes)
{
sbOutput.Append(Prim.ToString() + Environment.NewLine);
}
tbPrim.Text = sbOutput.ToString();
}
else
{
tbPrim.Text = "Operation canceled by user or Exception";
}
}
#endregion
Thank you Christofer,
I accepted you answer as you gave me some suggestions how to solve my problem.
You can see bellow how I solved this problem
Rad
//class variable
private SimulatorRunner simulatorRunner;
//Code behind DevicesSimulatorForm form
private void RunSimulator()
{
btnRerun.BackColor = Color.BurlyWood;
ParameterizedThreadStart start = new ParameterizedThreadStart(RunSimulator);
Thread simulatorProcessingThread = new Thread(start);
simulatorProcessingThread.Start(this);
}
//This will run in a separate thread so when accessing controls Invoke is being used.
public void RunSimulator(object form)
{
DevicesSimulatorForm devicesSimulatorForm = (DevicesSimulatorForm) form;
simulatorRunner.Run(devicesSimulatorForm);
devicesSimulatorForm.InvokeEx(formInner =>
{
formInner.btnRerun.BackColor = Color.LightGray;
InitializeFields();
InitializeTextBackBorder();
InitializeButtonControls();
running = false;
});
}
public class SimulatorRunner
{
public void Run(DevicesSimulator form)
{
string buffer = "Some content read from file in a loop that needs to be passed
to a rich text box when a boolean Intercept check box is true
and FormStatusIntercept will return true and with Thread.Sleep(1)
we will have a chance to update the buffer to the new value and by
unchecking Intercept check box we will exit while loop and continue
processing"
while (true)
{
if (FormStatusIntercept(form, ref buffer))
{
Thread.Sleep(1);
}
else
{
publishEventArgs.Buffer = buffer;
break;
}
}
PublishEvent?.Invoke(this, publishEventArgs);
}
}
private bool FormStatusIntercept(DevicesSimulator simulatorForm, ref string buffer)
{
string modifiedBuffer = buffer;
//When btnFormStatus button is pressed it changes FormStatusContinued = true
//which allows continuation of the processing by exiting while loop
if (simulatorForm.FormStatusContinued == true)
{
simulatorForm.InvokeEx(form =>
{
if (form.rtbFormStatus.Text != modifiedBuffer)
{
modifiedBuffer = form.rtbFormStatus.Text;
}
form.FormStatusContinued = false;
form.FormStatusInterceptPending = false;
});
buffer = modifiedBuffer;
return false;
}
else if (simulatorForm.FormStatusIntercept == true)
{
if (simulatorForm.FormStatusInterceptPending == false)
{
//Whith check box pressed (true) we request a stop
//and we enter a while loop with Thread.Sleep(1)
simulatorForm.InvokeEx(form =>
{
form.btnFormStatus.Text = "Continue";
form.rtbFormStatus.Text = modifiedBuffer;
form.FormStatusInterceptPending = true;
});
}
return true;
}
return false;
}

Limiting Number of Backgroundworkers to execute

I'm using multiple backgroundworker to execute process of connecting through network . Based on the number of rows in datagridview , it fetches each row values and passes through the another class method which perform long process. Here i'm using for loop to start the backgroundworker, so the problem is when the process starts, based on the count of datagridview rows more backgroundworkers starts to run. But i want to limit this . For example , if i have 50 rows , i just wants to execute 5 backgroundworkers at a time. After those 5 finishes the task the next 5 has to start . So how to limit those ?
I have used following code :
Backgroundworker bs;
private void button1_Click(object sender, EventArgs e)
{
threadNumber = 0;
threadRunning = 0;
for(int i=0;i<datagridview1.Rows.Count;i++)
{
bs = new Backgroundworker();
bs.DoWork += new DoWorkEventHandler(bs_DoWork);
bs.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bs_Completed);
bs.WorkerSupportCancellation = true;
bs.RunWorkerAsync(i);
}
}
private void bs_DoWork(object sender, DoWorkEventArgs e)
{
int i = (int)e.Argument;
while(bs.CancellationPending !=true && threadNumber < datagridview1.Rows.Count)
{
lock (this)
{
for (int i = 0; i < Max_Threads - threadRunning; i++)
{
if (threadNumber < datagridview1.Rows.Count)
{
Webmethod obj = new webmethod();
obj.Callmethod(i); // this takes long time to perform
threadNumber +=1;
threadRunning +=1;
}
}
}
}
}
But when i start the process, at the time all the backgroundworkers starts the task . Can anyone help me in this ?
Thanks in advance..
What you're trying to do isn't entirely clear. Your click method starts a new BackgroundWorker for each data row, but then your DoWork handler also executes the inner loop for each row. Very confusing. I think what you want is to start Max_Threads workers, and have them cooperate in processing the rows.
You have a problem with incrementing your threadNumber. Increment is not an atomic operation, so it's possible that two threads incrementing it concurrently will end up stepping on each other. You need to synchronize access to that, probably by using Interlocked.Increment.
I think what you want to do is this:
int threadNumber;
private void button1_Click(object sender, EventArgs e)
{
threadNumber = -1;
// Start Max_Threads workers
for(int i=0; i<Max_Threads; i++)
{
var bs = new Backgroundworker();
bs.DoWork += new DoWorkEventHandler(bs_DoWork);
bs.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bs_Completed);
bs.WorkerSupportCancellation = true;
bs.RunWorkerAsync();
}
}
private void bs_DoWork(object sender, DoWorkEventArgs e)
{
// Process items until cancellation requested, or until end of list
while(!bs.CancellationPending)
{
int rowNumber = Interlocked.Increment(ref threadNumber);
if (rowNumber >= datagridview1.Rows.Count)
{
break;
}
Webmethod obj = new webmethod();
obj.Callmethod(rowNumber); // this takes long time to perform
}
}

Categories