I have created a Form for long-running Tasks, it's purpose is to loop a Progress Bar until the Thread completes. I display this Form as a Dialog so that my main application is Waiting until this Form returns.
Unfortunately, my RunWorkerCompleted is being called before the Thread actually finishes its task.
What is an effective method of Waiting for a Thread to Finish, WITHOUT Joining?
public partial class FormProcessing : Form
{
public ThreadStart StartTaskFunc { get; set; }
public FormProcessing()
{
InitializeComponent();
bgWorker.DoWork += bgWorker_DoWork;
bgWorker.ProgressChanged += bgWorker_ProgressChanged;
bgWorker.RunWorkerCompleted += bgWorker_RunWorkerCompleted;
Shown += (s, e) =>
{
bgWorker.RunWorkerAsync();
};
}
void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Close();
}
void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
var thread = new Thread(() => StartTaskFunc());
thread.Start();
int i = 0;
while (thread.IsAlive)
{
if (i == 26)
i = 0;
bgWorker.ReportProgress(i);
Task.Delay(200).Wait();
i++;
}
}
}
You are creating another "Thread" in "bgWorker_DoWork" method.
If you create another thread, the "bgWorker_DoWork" is completed as soon as the new thread is created.
Related
I am attempting to use a Backgroundworker to keep my Main UI thread open and not freezing up. I am stepping thro my code and have set a breakpoint on both the backgroundWorker1.RunWorkerAsync(); which once hits just leaves the method and on the foreach line -> which is never hit.
What is the proper way to use a Backgroundworker?
public Form1()
{
InitializeComponent();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;
}
private void btnQuery_Click(object sender, EventArgs e)
{
grid1.Rows.Clear();
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
foreach (string name in studentRoster)
{
InsertIntoDB();
}
}
Here is your code with the handlers added and some comments.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;
backgroundWorker1.DoWork += new DoWorkEventHandler(BackgroundWorker_DoWork);
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(BackgroundWorker_ProgressChanged);
backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(BackgroundWorker_RunWorkerCompleted);
}
private void btnQuery_Click(object sender, EventArgs e)
{
grid1.Rows.Clear();
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
foreach (string name in studentRoster)
{
InsertIntoDB();
// You can report progress by calling the following function.
//backgroundWorker1.ReportProgress(int percentProgress, object userState)
// You can set the percentProgress to any valid integer value,
// and userState can be any object you want.
// You can also check to see if this operation has been sent a request to cancel.
if (backgroundWorker1.CancellationPending)
{
e.Cancel = true;
return;
}
}
// You can send information back to the main thread by setting e.Result to any object you want.
}
private void BackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// Do something with the event that is being raised.
// To pass a value back through to this event, use the percentProgress and userState
// parameters of the ReportProgress function.
// the userState object that you pass will be received here as e.UserState
}
private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// This event is raised by the background worker when the DoWork method is completed.
// You can receive information back from the worker thread by evaluating e.Result
}
}
}
I have main program
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Worker w1 = new Worker(1);
Worker w2 = new Worker(2);
Thread w1Thread = new Thread(new ThreadStart(w1.StartWorking));
Thread w2Thread = new Thread(new ThreadStart(w2.StartWorking));
w1Thread.Start();
w2Thread.Start();
Application.Run(new MainWindow());
if (w1Thread.IsAlive)
{
w1Thread.Abort();
}
if (w2Thread.IsAlive)
{
w2Thread.Abort();
}
}
}
and worker class:
class Worker
{
public int m_workerId;
public bool m_workerLifeBit;
public bool m_workerWork;
public Worker(int id)
{
m_workerId = id;
m_workerLifeBit = false;
}
public void StartWorking()
{
while (!m_workerWork)
{
m_workerLifeBit = false;
System.Threading.Thread.Sleep(5000);
m_workerLifeBit = true;
System.Threading.Thread.Sleep(5000);
}
}
}
I have checkBox on MainWindow form.
How to monitor state of Worker variable m_workerLifeBit and display its changes in MainWindow checkBox?
I have found this q&a How to update the GUI from another thread in C#? hovewer the answer does not show complete example, and I failed with using thread safe delegate.
I want some event mechanism that I fire in Worker.StartWorking and catch in slot in MainWindow form.
Here is a simple version using events:
class Worker
{
public event Action<bool> WorkerLifeBitChanged;
// ...
public void StartWorking()
{
// ...
m_workerLifeBit = false;
OnWorkerLifeBitChanged();
// ...
private void OnWorkerLifeBitChanged()
{
if (WorkerLifeBitChanged != null)
WorkerLifeBitChanged(m_workerLifeBit);
}
Then you wire up the event in Main:
//...
var mainWindow = new MainWindow();
w1.WorkerLifeBitChanged += mainWindow.UpdateWorkerLifeBit;
w2.WorkerLifeBitChanged += mainWindow.UpdateWorkerLifeBit;
w1Thread.Start();
w2Thread.Start();
Application.Run(mainWindow);
//...
And UpdateWorkerLifeBit implementation in MainWindow:
public void UpdateWorkerLifeBit(bool workerLifeBit)
{
if (this.checkBox.InvokeRequired)
{
this.Invoke(new Action(() => checkBox.Checked = workerLifeBit));
}
else
{
checkBox.Checked = workerLifeBit;
}
}
As mentioned in the comments, if this is a WinForms application then I'd recommend using a BackgroundWorker.
Kicking off the bg worker and subscribing to events:
BackgroundWorker worker = new BackgroundWorker();
// Subscribing to the worker method. Do all of your work here
worker.DoWork += worker_DoWork;
// Subscribing to the progress changed event where you'll want to update the UI
worker.ReportProgress = true;
worker.ProgressChanged += worker_ProgressChanged;
// Subscribing to the worker completed event. Fires when the work is complete
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
// This line starts the worker
worker.RunWorkerAsync();
You would then have your methods defined as such:
void worker_DoWork(object sender, DoWorkEventArgs e)
{
// Perform some work with the object you've passed in e.g.
MyObj foo = (MyObj)e.Argument;
foo.Name = "foobar";
// Notify UI
worker.ReportProgress(100, foo);
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// Update UI
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Worker has finished
}
One solution would be passing a reference of your Program class (or even a delegate in your program class, or a data reference in your worker class) to the Worker thread. You can call a function of your Program directly from the thread code then. You can also use signals, but for this small example my previous "solution" is acceptable.
It's a C# winform .Net framework 2.0 project: The time-consuming network transferring jobs run in a BackupgroundWorker. In this backgroundwork thread, SynchronizationContext method is used to send the current job title to a message dialog in the main UI thread. However, in my code, thread racing problem happens and "NullReferenceException" is given sometime (not always, but randomly). Here is my code:
private void DoBtn_Click(object sender, EventArgs e)
{
m_msgForm = new MsgForm(); //m_msgForm is a member variable of the class, and MsgForm is a form class with a "public static SynchronizationContext synContext"
m_msgForm.UpdateMsg("starting ..."); //UpdateMsg is public method to show progress information
BackgroundWorker myBackgroundWorker = new BackgroundWorker();
myBackgroundWorker.DoWork +=new DoWorkEventHandler(myBackgroundWorker_DoWork);
myBackgroundWorker.RunWorkerCompleted +=new RunWorkerCompletedEventHandler(myBackgroundWorker_RunWorkerCompleted);
myBackgroundWorker.RunWorkerAsync(theBackgroundArgument);
m_msgForm.ShowDialog(); //show as a modal dialog
}
And in the Background worker thread:
private void myBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
//... some code
string msgText ="doing job: " +job.Title;
RestoreMsgForm.synContext.Send(m_msgForm.UpdateMsg, msgText); //send message to m_msgForm in the UI thread
//... some code
}
The problem is : sometime the "RestoreMsgForm.synContext.Send() in the backgroundworker thread will run before m_msgForm.ShowDialog() in the UI thead.
And in this case, NullReferenceException will be throw.
How to solve this problem? thanks.
the definition of RestoreMsgForm is
public partial class RestoreMsgForm : Form
{
public static SynchronizationContext synContext;
public RestoreMsgForm()
{
InitializeComponent();
}
private void RestoreMsgForm_Load(object sender, EventArgs e)
{
synContext = SynchronizationContext.Current;
}
public void UpdateMsg(object msg)
{
msgLabel.Text = (string)msg;
}
}
See if it works like this instead:
private void DoBtn_Click(object sender, EventArgs e)
{
m_msgForm = new MsgForm(); //m_msgForm is a member variable of the class, and MsgForm is a form class with a "public static SynchronizationContext synContext"
m_msgForm.UpdateMsg("starting ..."); //UpdateMsg is public method to show progress information
BackgroundWorker myBackgroundWorker = new BackgroundWorker();
myBackgroundWorker.DoWork += new DoWorkEventHandler(myBackgroundWorker_DoWork);
myBackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(myBackgroundWorker_RunWorkerCompleted);
myBackgroundWorker.WorkerReportsProgress = true;
myBackgroundWorker.ProgressChanged += new ProgressChangedEventHandler(myBackgroundWorker_ProgressChanged);
myBackgroundWorker.RunWorkerAsync(theBackgroundArgument);
}
private void myBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = (BackgroundWorker)sender;
worker.ReportProgress(-1);
//... some code
string msgText = "doing job: " + job.Title;
worker.ReportProgress(0, msgText);
//... some code
worker.ReportProgress(0, "...other text...");
//... some code
worker.ReportProgress(0, "...etc...");
}
void myBackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (e.ProgressPercentage == -1)
{
m_msgForm.ShowDialog(); //show as a modal dialog
}
else
{
m_msgForm.UpdateMsg(e.UserState.ToString);
}
}
*Note that we have to set .WorkerReportsProgress = true for the BackgroundWorker() so we can use ReportProgress() and receive the ProgressChanged() event.
I'm running a C# forms application which starts a thread to acquire some data. This thread has some events inside it i.e: the events fire in the thread and are supposed to be captured by the same thread. However, the thread's events don't seem to be firing. Any clues?
private void btnPlay_Click(object sender, EventArgs e)
{
Thread thread = new Thread(kinect.onlineRun);
thread.IsBackground = true;
thread.Start();
}
inside the thread:
void PointCreated(object sender, IdEventArgs e) // a certain event that should fire and it doesn't
{
Console.WriteLine("Event Fired!");
}
public void onlinerun()
{
Console.WriteLine("run started"); // this is printed on console
while (true)
{
do_some_work();
//this work could result in the PointCreated event firing
}
}
Give something like this a try:
Assuming your calling class is called Controller and your delegate is called ControlEventHandler...
private void PointCreated(object sender, IdEventArgs e)
{
// Ensure the event was received in the calling thread
if (this.InvokeRequired)
{
if (e != null)
{
// We aren't in the correct thread so pass on the event
this.BeginInvoke(new Controller.ControllerEventHandler(this.PointCreated), new object[] { sender, e });
}
}
else
{
lock (this)
{
Console.WriteLine("Event Fired!");
// TODO: Do some stuff here
}
}
}
I have sth like that. It's giving me error. I cut out all unneeded parts of code. It is giving me this error
The calling thread cannot access this object because a different thread owns it.
public partial class MainWindow : Window
{
BackgroundWorker worker;
Grafik MainGrafik;
double ProgressBar
{
set { this.progressBarMain.Value = value; }
}
public MainWindow()
{
InitializeComponent();
worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
MainGrafik = new Grafik();
MainGrafik.ProgressUpdate +=
new Grafik.ProgressUpdateDelegate(MainGrafik_ProgressUpdate);
worker.RunWorkerAsync();
}
void MainGrafik_ProgressUpdate(double progress)
{
ProgressBar = progress;
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
while(true)
{
MainGrafik.Refresh();
Thread.Sleep(2000);
}
}
}
class Grafik
{
public delegate void ProgressUpdateDelegate(double progress,
DateTime currTime);
public event ProgressUpdateDelegate ProgressUpdate;
public void Refresh()
{
ProgressUpdate(5); // Just for testing
}
}
You can't update UI objects from another thread. They have to be updated in the UI thread. Try adding this code to the MainGrafik_ProgressUpdate(double progress)
void MainGragfik_ProgressUpdate(double progress)
{
if (InvokeRequired)
{
BeginInvoke((MethodIvoker)(() =>
{
MainGragfik_ProgressUpdate(progress);
}));
return;
}
ProgressBar = progress;
}
The thread firing the ProgressUpdate event is your BackgroundWorker. The ProgressUpdate event handlers are likely running on that thread, and not the UI thread.
in short call this on the form in the context of your other thread's execution:
void MainGrafik_ProgressUpdate(object sender, EventArgs e) {
Action<T> yourAction =>() yourAction;
if(yourForm.InvokeRequired)
yourForm.Invoke(yourAction);
else yourAction;
}
Or with MethodInvoker (blank delegate)
void MainGrafik_ProgressUpdate(object sender, EventArgs e) {
MethodInvoker invoker = delegate(object sender, EventArgs e) {
this.ProgressBar = whatever progress;
};
}