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.
Related
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();
}
}
}
}
I have three commands to execute waiting for each command to finish before starting the next one.
Based on my implementation after completing the first one the second one will start but backgroundWorker1_RunWorkerCompleted won't raise at all.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace cmd_commands
{
public partial class Form1 : Form
{
string[] commands = new string[] {
#"test",
#"test1",
#"test2" };
int command = 0;
public Form1()
{
InitializeComponent();
}
public void runCmd(string command)
{
ProcessStartInfo cmdsi = new ProcessStartInfo("cmd.exe");
cmdsi.Arguments = command;
Process cmd = Process.Start(cmdsi);
cmd.WaitForExit();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
runCmd(commands[command]);
backgroundWorker1.ReportProgress(0, command);
}
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label1.Text = "Working on command number: " + e.UserState.ToString();
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
command++;
runCmd(commands[command]);
}
}
}
BackgroundWorker is one time usage only i.e once the state is Completed it wont restart, u need to re-instantiate it.
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
command++;
if (command < commands.Length)
{
backgroundWorker1 = new BackgroundWorker();
backgroundWorker1.DoWork += this.backgroundWorker1_DoWork;
backgroundWorker1.ProgressChanged += this.backgroundWorker1_ProgressChanged;
backgroundWorker1.RunWorkerCompleted += this.backgroundWorker1_RunWorkerCompleted;
backgroundWorker1.RunWorkerAsync();
}
}
I have a Windows Mobile 6.5 App under development. When the user opens the App a dialog box appears with the login form. User logs in and then after 30 seconds (small time in production) when the timer has run out without activity I show the login Dialog box again using events:
static private void _TimerTick(object state)
{
// the user has been inactive for 30 secs; log him out
MainForm.timer = null;
using (LoginForm LoginForm = new LoginForm())
{
if (LoginForm.ShowDialog() == DialogResult.OK)
{
MainForm.timer = new System.Threading.Timer(_TimerTick, null, 1000 * 30 * 1, Timeout.Infinite);
}
else
{
Application.Exit();
}
}
}
But once I press login and return with an ok button from the login form the original form does show. Although it is still in the task manager. I have tried:
.TopMost = true; but then I can't assess the windows button in the bar at the bottom of the app and no other apps can run as form in my app is always in front of it.
A simple solution as this is only for the login and one main form:
LoginForm.cs (with two textboxes and main menu with Login and Exit):
using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace LoginFormTest
{
public partial class LoginForm : Form
{
public LoginForm()
{
InitializeComponent();
}
public void doShow(bool bShow)
{
if(bShow)
Invoke(new Action(() => this.Show()));
else
Invoke(new Action(() => this.Hide()));
}
public void doClose()
{
Invoke(new Action(() => this.Close()));
}
private void mnuLogin_Click(object sender, EventArgs e)
{
MainForm mainForm = new MainForm();
mainForm.Show();
System.Diagnostics.Debug.WriteLine("mainForm started");
}
}
}
Nothing special there.
The MainForm has code that will close the form if no activity:
using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace LoginFormTest
{
public partial class MainForm : Form
{
int countDown = 3; //number of seconds for timeout
System.Threading.Timer timer;
object lockCounter = new object(); //to sync access to counter var
public MainForm()
{
InitializeComponent();
//start a independent timer after 1000ms and with a 1000ms interval
timer = new System.Threading.Timer(new TimerCallback(this.timerCallback), null, 1000, 1000);
}
private void mnuExit_Click(object sender, EventArgs e)
{
doClose();
}
private void mnuLogout_Click(object sender, EventArgs e)
{
doClose();
}
private void doClose()
{
System.Diagnostics.Debug.WriteLine("mainForm closing");
try
{
timer.Dispose(); //else timer thread will continue running!
Invoke(new Action(() => this.Close()));
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("Exception in doClose(): " + ex.Message);
}
}
private void MainForm_MouseMove(object sender, MouseEventArgs e)
{
resetTimeout();
}
private void MainForm_KeyPress(object sender, KeyPressEventArgs e)
{
resetTimeout();
}
private void MainForm_Click(object sender, EventArgs e)
{
resetTimeout();
}
public void resetTimeout()
{
System.Diagnostics.Debug.WriteLine("resetTimeout()");
lock(lockCounter)
countDown = 3;
}
public void timerCallback(object stateInfo)
{
lock (lockCounter)
countDown--;
if (countDown == 0)
{
System.Diagnostics.Debug.WriteLine("timeout->doClose()");
doClose();
}
}
private void MainForm_Closed(object sender, EventArgs e)
{
System.Diagnostics.Debug.WriteLine("mainForm CLOSED");
}
}
}
Specials:
a lock object to sync access to the counter var
a threading timer that runs independent of the message pump
a delegate to by called from the TimerCallback function
i have created a form1 with button1 to start a task, which will be processed on the background with BackGroundWorker on a separate class and display a ProgressBar on a separate window.
my problem is the process is working fine except that the task is done yet progressbar form hangs on the middle.
below is BackGroundLoading Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.ComponentModel;
namespace sample2
{
public class BackgroundLoading
{
public BackgroundWorker Bw;
public delegate void RunFunction();
public RunFunction thisFunction;
mycontrols.LoadProgress p = new mycontrols.LoadProgress();
public BackgroundLoading(RunFunction newFunction)
{
thisFunction = newFunction;
Bw = new BackgroundWorker();
Bw.WorkerReportsProgress = true;
Bw.DoWork += new DoWorkEventHandler(Bw_DoWork);
Bw.ProgressChanged += new ProgressChangedEventHandler(Bw_ProgressChanged);
Bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Bw_RunWorkerCompleted);
}
public void Start()
{
Bw.RunWorkerAsync();
}
void Bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
MessageBox.Show("Error: " + e.Error.Message);
}
if (e.Cancelled)
{
MessageBox.Show("Cancelled!");
}
else
{
MessageBox.Show("Completed!");
p.Close();
}
}
public void Bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
/// If I put
/// p.ProgBar.Value = e.ProgressPercentage;
/// the progressbar hangs upon form show.
}
void Bw_DoWork(object sender, DoWorkEventArgs e)
{
p.Show();
BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 1; (i <= 100); i++)
{
if ((worker.CancellationPending == true))
{
e.Cancel = true;
break;
}
else
{
if (thisFunction != null)
{
thisFunction();
worker.ReportProgress((i * 1));
p.ProgBar.Value = i;
}
else
{
MessageBox.Show("Error, no method found");
}
}
}
}
}
}
this is the Form1 code.
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;
namespace sample2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
BackgroundLoading BL = new BackgroundLoading(workingmethod);
BL.Start();
}
private void workingmethod()
{
System.Threading.Thread.Sleep(10);
}
}
}
this is the code for the LoadProgress form
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;
namespace sample2.mycontrols
{
public partial class LoadProgress : Form
{
public LoadProgress()
{
InitializeComponent();
}
private void LoadProgress_Load(object sender, EventArgs e)
{
}
}
}
no code on the progressbar form since update is done on the class.
can't find the causing of hanging.
Thanks in Advance.
Show the form on the main UI thread, immediately prior to running your BackgroundWorker:
public void Start()
{
p.Show();
Bw.RunWorkerAsync();
}
Then you can make calls to the form in the ProgressChanged event, which runs on the UI thread.
Remove p.Show() and p.ProgBar.Value = i; from the DoWork event - you don't want to touch the UI thread from your background thread.
public void Bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
p.ProgBar.Value = e.ProgressPercentage;
}
Finally, close the form when the BackgroundWorker is complete (which you're already doing):
void Bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
p.Close();
}
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);