How can i stop/resume a backgroundworker/recursive loop? - c#

In the top of the form1 i added:
System.Threading.ManualResetEvent _busy = new System.Threading.ManualResetEvent(true);
And also in the top of the form1 added two flags:
bool pause;
bool resume;
Both of the flags are set to false in the constructor.
Then i have a function that is recursive loop:
private List<string> test(string url, int levels,DoWorkEventArgs eve)
{
if (pause == true)
{
_busy.Reset();
}
if (resume == true)
{
_busy.Set();
}
this.Invoke(new MethodInvoker(delegate { label3.Text = label3.Text = (Int32.Parse(label12.Text) + Int32.Parse(label10.Text)).ToString(); }));
HtmlWeb hw = new HtmlWeb();
List<string> webSites;
List<string> csFiles = new List<string>();
csFiles.Add("temp string to know that something is happening in level = " + levels.ToString());
csFiles.Add("current site name in this level is : " + url);
try
{
this.Invoke(new MethodInvoker(delegate { ColorText.Texts(richTextBox1, "Loading The Url: " , Color.Red); }));
this.Invoke(new MethodInvoker(delegate { ColorText.Texts(richTextBox1, url + "...",Color.Blue); }));
HtmlAgilityPack.HtmlDocument doc = TimeOut.getHtmlDocumentWebClient(url, false, "", 0, "", "");
this.Invoke(new MethodInvoker(delegate { ColorText.Texts(richTextBox1, " Done " + Environment.NewLine, Color.Red); }));
currentCrawlingSite.Add(url);
webSites = getLinks(doc);
removeDupes(webSites);
removeDuplicates(webSites, currentCrawlingSite);
removeDuplicates(webSites, sitesToCrawl);
if (removeExt == true)
{
for (int i = 0; i < webSites.Count; i++)
{
webSites.Remove(removeExternals(webSites));
}
}
if (downLoadImages == true)
{
webContent.retrieveImages(url); }
if (levels > 0)
sitesToCrawl.AddRange(webSites
this.Invoke(new MethodInvoker(delegate { label7.Text = sitesToCrawl.Count.ToString(); }));
this.Invoke(new MethodInvoker(delegate { label12.Text = currentCrawlingSite.Count.ToString(); }));
if (levels == 0)
{
return csFiles;
}
else
{
for (int i = 0; i < webSites.Count(); i++)//&& i < 20; i++) {
string t = webSites[i];
if ((t.StartsWith("http://") == true) || (t.StartsWith("https://") == true)) // replace this with future FilterJunkLinks function
{
csFiles.AddRange(test(t, levels - 1, eve));
}
}
return csFiles;
}
}
catch
{
failedUrls++;
this.Invoke(new MethodInvoker(delegate { label10.Text = failedUrls.ToString(); }));
this.Invoke(new MethodInvoker(delegate { ColorText.Texts(richTextBox1, " Failed " + Environment.NewLine, Color.Green); }));
return csFiles;
}
}
In the backgroundworker DoWork event i added this:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
_busy.WaitOne();
this.Invoke(new MethodInvoker(delegate { label2.Text = "Website To Crawl: "; }));
this.Invoke(new MethodInvoker(delegate { label4.Text = mainUrl; }));
test(mainUrl, levelsToCrawl, e);
}
Then i have two buttons click events one for pause one for resume:
private void button4_Click(object sender, EventArgs e)
{
pause = true;
}
private void button5_Click(object sender, EventArgs e)
{
resume = true;
}
But when i click on the button pause nothing happens either on the resume button. The process is keep going on. I tried also without flags just in each button to make _busy.Reset(); and _busy.Set(); but nothing.
What i want is to pause somehow the recursive loop and to resume it. Im not sure if its connected to the backgroundworker or not but the idea is to pause and resume the process.
What should i do ?
Thanks.

_busy.WaitOne();
That's the statement that would cause the worker to pause. However, it only appears at the start of DoWork(), where the event will never be reset yet. You'll need to move it inside the loop.
The second bug, one you haven't run into yet, is that once you paused you can never resume again. Because the worker is not running anymore and won't be paying attention to the resume variable. Get rid of those variables, your Click event should directly call the Set() and Reset() methods.

Related

Sleep Thread With IF condition in C#

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.

Multithread using backgroundworker and event handler

I'm developing a sample program to connect multiple device using backgroundworker. Each device connected will be add to the list as new object. After finished connecting all the devices, i wanted to add an event handler for each connected devices. The problem that i'm facing now is the event handler doesn't firing at all. Below are the sample codes.
The Connect click button event :
private void btnConnect_Click(object sender, EventArgs e)
{
using (BackgroundWorker m_oWorker = new BackgroundWorker())
{
m_oWorker.DoWork += delegate (object s, DoWorkEventArgs args)
{
int iIpStart = 0;
int iIpEnd = 0;
string strIp1 = string.Empty;
string strIp2 = string.Empty;
list.Clear();
string[] sIP1 = txtIpStart.Text.Trim().ToString().Split('.');
string[] sIP2 = txtIpEnd.Text.Trim().ToString().Split('.');
iIpStart = Convert.ToInt32(sIP1[3]);
iIpEnd = Convert.ToInt32(sIP2[3]);
strIp1 = sIP1[0] + "." + sIP1[1] + "." + sIP1[2] + ".";
strIp2 = sIP2[0] + "." + sIP2[1] + "." + sIP2[2] + ".";
Ping ping = new Ping();
PingReply reply = null;
int iIncre = 0;
int iVal = (100 / (iIpEnd - iIpStart));
for (int i = iIpStart; i <= iIpEnd; i++)
{
Thread.Sleep(100);
string strIpconnect = strIp1 + i.ToString();
Console.Write("ip address : " + strIpconnect + ", status: ");
reply = ping.Send(strIpconnect);
if (reply.Status.ToString() == "Success")
{
if (ConnectDevice(strIpconnect))
{
strLastDevice = strIpconnect + " Connected";
isconnected = true;
}
else
{
isconnected = false;
}
}
else
{
isconnected = false;
}
m_oWorker.ReportProgress(iIncre);
iIncre = iIncre + iVal;
}
m_oWorker.ReportProgress(100);
};
m_oWorker.ProgressChanged += new ProgressChangedEventHandler(m_oWorker_ProgressChanged);
m_oWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(m_oWorker_RunWorkerCompleted);
m_oWorker.WorkerReportsProgress = true;
m_oWorker.WorkerSupportsCancellation = true;
m_oWorker.RunWorkerAsync();
}
}
ConnectDevice function method. Connected device will be added to the list :
protected bool ConnectDevice(string sIP)
{
try
{
NewSDK sdk = new NewSDK();
if (sdk.Connect() == true)
{
list.Add(new objSDK { sdk = sdk, ipaddress = sIP });
return true;
}
else
{
}
}
catch() {}
return false;
}
the Backgroundworker :
void m_oWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//If it was cancelled midway
if (e.Cancelled)
{
lblStatus.Text = "Task Cancelled.";
}
else if (e.Error != null)
{
lblStatus.Text = "Error while performing background operation.";
}
else
{
lblStatus.Text = "Task Completed...";
btnListen.Enabled = true;
}
}
void m_oWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//Here you play with the main UI thread
progressBar1.Value = e.ProgressPercentage;
lblStatus.Text = "Processing......" + progressBar1.Value.ToString() + "%";
if (isconnected)
{
listBox2.Items.Add(strLastDevice);
string[] ssplit = sDeviceInfo.Split(';');
foreach (string sword in ssplit)
{
listBox1.Items.Add(sword);
}
}
}
The function to attached event :
private void RegisterEvent()
{
foreach (objSDK obj in list)
{
obj.sdk.OnTransaction += () =>
{
listBox1.Items.Add("ip : " + obj.IP + " transaction");
};
}
}
You have declared m_oWorker as a local variable. I'm guessing this was a mistake ( the m_ prefix should only be used for class member variables)?
Also, you declared it within a using statement, meaning that it that the framework will call Dispose() on it at the end of the using block. Even if you held on to a reference to it (and I don't think you do) it still means its resources will be deallocated, which is probably why it isn't handling any events.
I try another workaround by using thread and task and work perfectly. Thanks for all response

Cross thread operation not valid when use backgroundworker in c#

private bool ImportData()
{
bool result = false;
try
{
intdevid = int.Parse(cmbDeviceName.SelectedValue.ToString());
FetchDevicedata(intdevid);
//FTPTCompletedBatchTransfer();
FetchMaxReportId();
GetFTPFile(strDeviceIP, strDeviceUsername, strDevicePwd, strDevicePath + "//RunningBatch//RunningBatch.db", "RunningBatch.db"); // Copy RunningBatch.db to Debug Folder from Remote
LoadRunningData(); // Get Running Data in dataset from running.db
if (DecodeBatchData_R() == false)
{
MessageBox.Show("Running Batch Data Not Found");
}// save in batch master and row data table
GetFTPFile(strDeviceIP, strDeviceUsername, strDevicePwd, strDevicePath + "//CompletedBatch//CompletedBatch.db", "CompletedBatch.db");
LoadCompletedData();
if (DecodeBatchData() == false)
{
MessageBox.Show("Completed Batch Data not found");
}
result = true;
}
catch (Exception ex)\\here error:Cross-thread operation not valid: Control 'cmbDeviceName' accessed from a thread other than the thread it was created on.
{
clsLogs.LogError("Error: " + ex.Message + this.Name + " || ImportData");
result = false;
}
return result;
}
private void btnimport_Click(object sender, EventArgs e)
{
//////////////////copy checkweigher .db to database folder
dsCheckRptId = new DataSet();
///////////////////////////////////////////////////////////
if (cmbDeviceName.Text.ToString().Trim() == "--Select--")
{
MessageBox.Show("Please Select Proper Device");
cmbDeviceName.Focus();
return;
}
var deviceId = (int)cmbDeviceName.SelectedValue;
bgw.RunWorkerAsync(deviceId);
progressBar1.Visible = true;
label2.Visible = true;
}
void bgw_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i <= 100; i++)
{
var deviceId = (int)e.Argument;
e.Result = ImportData();
System.Threading.Thread.Sleep(100);
bgw.ReportProgress(i);
}
}
void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
label2.Text = String.Format("Progress: {0} %", e.ProgressPercentage);
}
void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
var result = (bool)e.Result;
if (cmbDeviceName.SelectedValue != null && cmbDeviceName.SelectedValue.ToString().Trim() != "0" && cmbDeviceName.SelectedValue.ToString().Trim() != "System.Data.DataRowView" && cmbDeviceName.SelectedValue.ToString().Trim() != "")
if (result)
{
MessageBox.Show("Data Import Completed Successfully for " + strDevicename);
clsLogs.LogEvent(3, "Data Import Completed Successfully for " + strDevicename);
}
else
{
MessageBox.Show("Data Import Fail For " + strDevicename);
clsLogs.LogEvent(3, "Data Import Fail for " + strDevicename);
}
progressBar1.Visible = false;
label2.Visible = false;
}
;When I run this background worker coding, there's an error stating "Cross-thread operation not valid: Control 'cmbDeviceName' accessed from a thread other than the thread it was created on. ."
How do I solve this problem guys?
WinForms controls are not thread safe, thus cross-thread operations on controls are not valid. You can access controls only from thread which created those controls. In your code you are accessing cmbDeviceName combobox from background thread. Best option to solve this is passing intdevid as RunWorkerAsync argument:
// executed on main thread
var deviceId = (int)cmbDeviceName.SelectedValue;
backgroundWorker.RunWorkerAsync(deviceId);
And get this argument in your DoWork handler:
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// executed on background thread
var deviceId = (int)e.Argument;
// ...
}
Suggested reading: Safe, Simple Multithreading in Windows Forms

How do i use a progressBar to perform an progress for each process in my program?

In the top of the form i did:
progressBar1.Maximum = 100;
progressBar1.Minimum = 1;
Then in the button click event that start the operation i did:
timer2.Enabled = true;
if (this.backgroundWorker1.IsBusy == false)
{
this.backgroundWorker1.RunWorkerAsync();
}
Then in the backgroundworkerdowork event:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
if (filesContent.Length > 0)
{
for (int i = 0; i < filesContent.Length; i++)
{
File.Copy(filesContent[i], Path.Combine(contentDirectory, Path.GetFileName(filesContent[i])), true);
}
}
DoProgressBar(e, worker);
WindowsUpdate();
CreateDriversList();
GetHostsFile();
Processes();
}
All the functions in the DoWork event are copying files the Processes() function use a new class i did that use Process to create/copy files.
Then the new DoProgressBar event function i did:
private static void DoProgressBar(DoWorkEventArgs e, BackgroundWorker worker)
{
for (int i = 1; i <= 90; i++)
{
if ((worker.CancellationPending == true))
{
e.Cancel = true;
break;
}
else
{
// Perform a time consuming operation and report progress.
System.Threading.Thread.Sleep(50);
worker.ReportProgress(i);
}
}
}
Then ProgressChanged event:
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
Then the completed event:
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if ((e.Cancelled == true))
{
this.label1.Text = "Canceled!";
}
else if (!(e.Error == null))
{
this.label1.Text = ("Error: " + e.Error.Message);
}
else
{
this.progressBar1.Value = this.progressBar1.Maximum;
processfinish = true;
}
}
Timer2 tick event:
private void timer2_Tick(object sender, EventArgs e)
{
timerCount += 1;
TimerCount.Text = TimeSpan.FromSeconds(timerCount).ToString();
TimerCount.Visible = true;
if (processfinish == true)
{
timer2.Enabled = false;
timer1.Enabled = true;
}
}
And timer1 tick event:
private void timer1_Tick(object sender, EventArgs e)
{
count++;
Diagnose.Text = "PROCESS HAS FINISHED" + " " + countBack--;
if (count == 6)
{
Diagnose.Text = "COLLECT INFORMATION";
Diagnose.Enabled = true;
CreateZip.Enabled = true;
ViewLogFile.Enabled = true;
DriverVerifier.Enabled = true;
timer1.Enabled = false;
TimerCount.Visible = false;
}
}
I know its a long code but everything here is connected.
What i wanted to do is that the progressBar will get progress according to the progress of each function in the DoWork event .
But instead what is it doing now is first going to the :
DoProgressBar() event/function do the second/else part ReportProgress(i)
Then its going to the Progresschanged event and do: progressBar1.Value = e.ProgressPercentage;
The result is when i click the button click to start the opertion i see right away the progress bar moving almost to the end instead moving according to each function/progress of the program.
You can see my complete code of Form1 here:
http://codepaste.net/fuk9w5
EDIT:
This is the code of the class ProcessRun where im using in Form1 in the function Processes()
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.IO;
namespace Diagnostic_Tool_Blue_Screen
{
class ProcessRun
{
public void ProcessesRun()
{
}
public static void Processing(string WorkingDirectory, string FileName, string Arguments, bool StandardOutput, string OutputFileName)
{
Process proc = new Process();
proc.EnableRaisingEvents = true;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.RedirectStandardOutput = StandardOutput;
proc.StartInfo.FileName = FileName;
proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.WorkingDirectory = WorkingDirectory;
proc.StartInfo.Arguments = Arguments;
proc.Start();
if (StandardOutput == true)
{
string output = proc.StandardOutput.ReadToEnd();
DumpOutput(WorkingDirectory + "\\" + OutputFileName, output);
}
proc.WaitForExit();
proc.Close();
}
private static void DumpOutput(string filename, string output)
{
StreamWriter w = new StreamWriter(filename);
w.Write(output);
w.Close();
}
}
}
It appears that your background thread is interacting directly with a UI element (the progress bar). That's a problem. Your background thread can't directly interact with the UI element; it has to invoke it such that the UI update occurs on the UI thread.
You could, for example, add a method like this to your form:
// Form method for updating progress bar; callable from worker thread
public void UpdateProgressBar(double progress)
{
// dispatch the update onto the form's thread
Dispatcher.BeginInvoke((Action<double>)((n) =>
{
// do the update in the form's thread
progressBar1.Value = n;
}), progress);
}
You can then call this method from your worker thread and the progress bar should update properly.
why not put this into your code? This is how I move the "green" part of the progressbar.
progressBar1.Step = pos; //where pos is the number on how much do you want to increase the progress of the progressbar
progressBar1.PerformStep(); //triggers the movement of the progressBar.
For those who ended up here while trying to find a way to change value of progess bar with cross threading this is how you do it:
form.Invoke((Action)delegate { form.function(); });

How can i sum the total of links in real time while its processing?

I have this code its a function that calling it self all the time.
Im using backgroundworker and in the backgroundworker Dowork event i call the function first time with a button click to start the process.
In this function i have two placesi m using label 12 and label 10 Text property.
Label 12 present bad links and Label 10 present good links. Bot are working in real time the text is changing in real time.
Where in this function i can use the label 3 Text to sum both label 12 + label 10 and present the total of links ?
Like this:
this.Invoke(new MethodInvoker(delegate { label3.Text = label12 + label10 }));
The questions are :
How do i do the sum of label12 and label10 so label3 will present a number int ?
Where should i put this label3.Text line in the function ?
This is the code:
private List<string> test(string url, int levels,DoWorkEventArgs eve)
{
HtmlWeb hw = new HtmlWeb();
List<string> webSites;
List<string> csFiles = new List<string>();
csFiles.Add("temp string to know that something is happening in level = " + levels.ToString());
csFiles.Add("current site name in this level is : " + url);
try
{
this.Invoke(new MethodInvoker(delegate { Texts(richTextBox1, "Loading The Url: " , Color.Red); }));
this.Invoke(new MethodInvoker(delegate { Texts(richTextBox1, url + "...",Color.Blue); }));
HtmlAgilityPack.HtmlDocument doc = TimeOut.getHtmlDocumentWebClient(url, false, "", 0, "", "");
this.Invoke(new MethodInvoker(delegate { Texts(richTextBox1, " Done " + Environment.NewLine, Color.Red); }));
currentCrawlingSite.Add(url);
webSites = getLinks(doc);
removeDupes(webSites);
removeDuplicates(webSites, currentCrawlingSite);
removeDuplicates(webSites, sitesToCrawl);
if (removeExt == true)
{
removeExternals(webSites);
}
if (downLoadImages == true)
{
webContent.retrieveImages(url); }
if (levels > 0)
sitesToCrawl.AddRange(webSites
this.Invoke(new MethodInvoker(delegate { label7.Text = sitesToCrawl.Count.ToString(); }));
this.Invoke(new MethodInvoker(delegate { label12.Text = currentCrawlingSite.Count.ToString(); }));
if (levels == 0)
{
return csFiles;
}
else
{
for (int i = 0; i < webSites.Count(); i++)//&& i < 20; i++)
{
string t = webSites[i];
if ((t.StartsWith("http://") == true) || (t.StartsWith("https://") == true)) // replace this with future FilterJunkLinks function
{
csFiles.AddRange(test(t, levels - 1, eve));
}
}
return csFiles;
}
}
catch
{
failedUrls++;
this.Invoke(new MethodInvoker(delegate { label10.Text = failedUrls.ToString(); }));
this.Invoke(new MethodInvoker(delegate { Texts(richTextBox1, " Failed " + Environment.NewLine, Color.Green); }));
return csFiles;
}
}
To convert a string from a Label to a number, use the following:
int number = Int32.Parse(someLabel);
So your code should be:
label3.Text = (Int32.Parse(label12.Text) + Int32.Parse(label10.Text)).ToString();

Categories

Resources