Closing a form that is created in another thread - c#

I have been searching for an answer to my particular problem for a while with no success.
I have a task in my program that takes a few seconds and I want to show a new form while that task is being done. The new form has a loadingbar and some text.
I need to show the new form parallel to the task otherwise the task will not start untill I close the new form.
This is the solution I have now:
private void loadingBar()
{
frmLoading frm = new frmLoading("Please wait while the database is being backed up", "This might take several days.");
frm.ShowDialog();
}
public void Backup()
{
Thread load = new Thread(new ThreadStart(loadingBar));
load.Start();
///Execute a task.
load.Abort();
}
So, this works OK but my question is: Wouldn't it be better to close the the form "frm" in the load-thread to make it stop?

You could do this a few ways...
1 - You could do as BendEg suggested and invoke you frmClose once you are ready
Something like;
Invoke(new Action(Close));
or
Invoke(new Action(() => frmMain.Close()));
2 - Or you could simply use a background worker;
The simplest way to demonstrate this would be to add a BackgroundWorker to your form, and use the events provided;
public Form1()
{
InitializeComponent();
backgroundWorker1.RunWorkerAsync();
MessageBox.Show(#"Please wait while the database is being backed up", #"This might take several days.");
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
Debug.WriteLine("Running"); //Execute a task
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Debug.WriteLine("Ended"); //Dispose of any objects you'd like (close yor form etc.)
}
I hope this helps.

You can declare the form on Class-Level and later close it with an invoke.
MSDN-Windows Forms Invoke
Like this:
public class Class1
{
private Form myForm;
public Class1()
{
myForm = new Form();
}
public void DoSomeWork()
{
// ===================================================
// Do Some Work...
// ===================================================
myForm.Invoke(new MethodInvoker(this.Hide));
}
public void Hide()
{
myForm.Hide();
}
public void Backup()
{
myForm.ShowDialog();
Thread load = new Thread(new ThreadStart(DoSomeWork));
load.Start();
}
}

I think this can work for you.
void YourMethod()
{
WaitForm wf = new WaitForm();
Invoke(new PleaseWaitDelegate(Launch),wf);
bool val = BoolMethodDoWork();
Invoke(new PleaseWaitDelegate(Close), wf);
if(val)
{
MessageBox.Show("Success!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
return;
}
MessageBox.Show("Damn!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
}
delegate void PleaseWaitDelegate(Form form);
void Launch(Form form)
{
new System.Threading.Thread(()=> form. ShowDialog()).Start();
}
void Close(Form form)
{
form.Close();
}

I think this will help you (if i understood you right):
Parallel.Invoke(() => somemethod(), () =>
{
someothertaskmethod();
});
I placed methods as example to demonstrate 2 tasks running.
You nee to use the proper using statement using System.Threading.Tasks;

Related

How to suspend a thread by another thread in C#?

Sorry for my bad English. Hope someone suggests me a better version of my question.
I've searched but seemed like I couldn't find the answer for my problem.
Currently, I'm writing a C# WPF app. This app will perform a heavy task in a long time. So I've decided to create another class with that heavy method and pass that method to another thread. I have to create a class to do that because the heavy method takes parameters.
I want the ability to suspend and resume that thread. I've known that I should use a ManualResetEvent object or Thread.Sleep method.
After many hours of trying and testing, getting confused why I always end up suspend the UI thread but the heavy thread is still running. What I've tried were:
Create a ManualResetEvent object called mre inside the HeavyClass. When user click the Pause button, the UI class will call the method heavyClass.mre.WaitOne().
class HeavyClass
{
// properties
ManualResetEvent mre = new ManualResetEvent(false);
public void HeavyRun()
{
//Do something takes really long time
//And doesn't have any loops
}
}
class MainWindow : Window
{
// properties
private HeavyClass heavyClass = new HeavyClass();
private void buttonStart_Click(object sender, RoutedEventArgs e)
{
Thread t = new Thread(heavyClass.HeavyRun);
t.Start();
}
private void buttonPause_Click(object sender, RoutedEventArgs e)
{
heavyClass.mre.WaitOne();
}
}
Create a method called SleepThread inside the HeavyClass. When user click the Pause button, the UI class will call the method heavyClass.SleepThread().
class HeavyClass
{
//properties
ManualResetEvent mre = new ManualResetEvent(false);
public void SleepThread()
{
Thread.Sleep(Timeout.Infinite);
//mre.WaitOne();
//They are the same behavior
}
public void HeavyRun()
{
//Do something takes really long time
//And doesn't have any loops
}
}
class MainWindow : Window
{
// properties
private HeavyClass heavyClass = new HeavyClass();
private void buttonStart_Click(object sender, RoutedEventArgs e)
{
Thread t = new Thread(heavyClass.HeavyRun);
t.Start();
}
private void buttonPause_Click(object sender, RoutedEventArgs e)
{
heavyClass.SleepThread();
}
}
Create an EventHandler<MainWindow> PauseThread inside the UI class, then write its handle inside the HeavyClass. When user click the Pause button, the UI class will trigger the event PauseThread(this, this).
class MainWindow : Window
{
// properties
private HeavyClass heavyClass = new HeavyClass();
public event EventHandler<MainWindow> PauseThread;
private void buttonStart_Click(object sender, RoutedEventArgs e)
{
Thread t = new Thread(heavyClass.HeavyRun);
t.Start();
}
private void buttonPause_Click(object sender, RoutedEventArgs e)
{
PauseThread(this, this);
}
}
class HeavyClass
{
// properties
ManualResetEvent mre = new ManualResetEvent(false);
public void HeavyRun()
{
MainWindow.PauseThread += (s, E) =>
{
Thread.Sleep(Timeout.Infinite);
//mre.WaitOne();
//They are the same behavior
};
//Do something takes really long time
//And doesn't have any loops
}
}
As I said above, I always paused the UI thread and the heavy task is still running.
And finally in the end, I've known the essence of my problem. That is: which thread calls Thread.Sleep() or WaitOne() will be blocked. Yeah, "which thread", not "which class".
Everything makes sense for me now. But that doesn't help me to achieve my goal. And that leads me to think that I am doing the seemingly impossible thing. It's clearly that I want to pause a thread by another thread. But that another thread is the one who calls any kinds of "suspend thread", so it is the one who is suspended. I don't have any idea about how to make the heavy method to be suspended by itself. It is running, how the hell it could know when the user click the Pause button?
I am at a total loss. Someone please help me to make my app works as expected.
By the way, this impossible thing makes me think that I am doing things wrong way, is it?
UPDATE: If you like to see my heavy task, actually it is very simple
class HeavyClass
{
public string filePath = "D:\\Desktop\\bigfile.iso";//This file is about 10GB
public string HeavyRun()
{
string MD5Hash;
MD5 md5 = MD5.Create();
Stream stream = File.OpenRead(filePath);
MD5Hash = Encoding.Default.GetString(md5.ComputeHash(stream));
return MD5Hash;
}
}
To make a thread suspendable, the work in the thread must be separable. In your case md5.ComputeHash(stream) will do all the work, and there is not way to make sure that thread will suspend at a right(saft) point inside md5.ComputeHash(stream). So you have to rewrite HeavyClass like below. Please notice that those codes are not the best approach of handling a thread, and I just try to keep it as same as the original.
class HeavyClass
{
MD5 _md5 = MD5.Create();
MethodInfo _hashCoreMI = _md5.GetType().GetMethod("HashCore", BindingFlags.NonPublic | BindingFlags.Instance);
MethodInfo _HashFinalMI = _md5.GetType().GetMethod("HashFinal", BindingFlags.NonPublic | BindingFlags.Instance);
WaitHandle _signal;
public void HeavyClass(WaitHandle signal)
{
_signal = signal;
}
public string HeavyRun(string filename)
{
byte[] buffer = new byte[4096];
int bytesRead = 0;
_signal.Set();
using(FileStream fs = File.OpenRead(filename))
{
while(true)
{
bytesRead = fs.Read(buffer, 0, 4096);
if (bytesRead > 0)
{
_hashCoreMI.Invoke(_md5, new object[] { buffer, 0, bytesRead });
}
else
{
break;
}
// if WaitHandle is signalled, thread will be block,
// otherwise thread will keep running.
_signal.WaitOne();
}
}
byte[] hash = _hashFinalMI.Invoke(_md5, null);
_md5.Initialize();
return Encoding.ASCII.GetString(hash);;
}
}
class MainWindow : Window
{
private HeavyClass _heavyClass;
private ManualResetEvent _mre;
public MainWindow()
{
InitializeComponent();
_mre = new ManualResetEvent(true);
_heavyClass = new HeavyClass(_mer);
}
private void buttonStart_Click(object sender, RoutedEventArgs e)
{
Thread t = new Thread(heavyClass.HeavyRun("D:\\Desktop\\bigfile.iso"));
t.Start();
}
private void buttonPause_Click(object sender, RoutedEventArgs e)
{
_mre.Reset();
}
private void buttonResume_Click(object sender, RoutedEventArgs e)
{
_mre.Set();
}
}

C# Form Controls Won't Update with Multithreading

I've been playing around with multithreading and reading up on some of the questions here, but I haven't found an answer that directly addresses my concerns here.
I have an application that runs on a single thread, except for a progress bar in a separate window. Based on my research, I need to create a new thread for that form which will redraw the form's controls as it's properties change. I've reduced the problem to a simple example below:
Here's the 'main' program:
class Program
{
static MyForm form;
static void Main(string[] args)
{
form = new MyForm();
form.Show();
doWork();
form.Close();
}
//arbitrary example of processing that takes some period of time
static void doWork()
{
while (form.Value < 100000)
{
form.ChangeVal();
Thread.Sleep(1);
}
return;
}
}
...And here's the Form. I'm not including the auto-generated stuff from VS.
public partial class MyForm : Form
{
private int val;
public int Value
{
get { return val; }
set { val = value; }
}
public Thread GUIupdater;
public MyForm()
{
InitializeComponent();
this.Refresh();
}
private void MyForm_Load(object sender, EventArgs e)
{
GUIupdater = new Thread(new ThreadStart(GUIupdaterThread));
GUIupdater.Start();
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(killThreadOnClose);
}
public void ChangeVal()
{
val++;
}
private void changeLabel(string s)
{
label.Text = s;
label.Refresh();
}
private delegate void labelChanger(string s);
private void GUIupdaterThread()
{
while (true)
{
Invoke(new labelChanger(changeLabel), new object[]{val.ToString()} );
Thread.Sleep(100); //100 ms
}
}
private void killThreadOnClose(object sender, FormClosingEventArgs e)
{
GUIupdater.Abort();
}
}
So, my intention here is to have the calculations running constantly, with the window's graphics updating reasonably quickly. When I run the program, however, the invoke function is only called once, and the label never actually updates!
Any and all feedback is appreciated. If you want to view the code in an IDE you can download my project from Here
Edits:
When I add Console.WriteLine Statements, I discovered that the GUIupdaterThread (the thing that's meant to update the GUI) loop always 'breaks' on the Invoke statement, never reaching 'Thread.Sleep'. I changed it to 'BeginInvoke', which causes the loop to function properly, but this hasn't changed the fact that the GUI doesn't update.
CLARIFICATIONS:
About my 'actual' project:
The main thread here in 'Program' simulates my software, which is a plugin implementing an interface. My decision to alter val / value in that thread, not in the thread created by the window, was deliberate.
I'm constrained to using .NET 4.0 . any more recent features can't help me
Since in your application you have GUI thread (main thread) - all UI controls will be accessible from this thread only.
There are several approaches how to update controls from other threads.
I would like to recommend you to use one of modern and native approaches based on Progress < T > class (it's native for .Net platform).
I would suggest overriding the form's OnPaint method. Then inside ChangeVal, after you have updated whatever variables/data you need to update, call this.Invalidate which should trigger the form to repaint itself.
Or if you're just updating a single label, call label.Refresh in your ChangeVal method. The form should update correctly. Here's an example that worked for me:
This form has a single label on it.
public partial class ProgressForm : Form
{
private int currentValue = 0;
public ProgressForm()
{
InitializeComponent();
}
public void ChangeValue(int newValue)
{
currentValue = newValue;
lblValue.Text = string.Format("Current value: {0}", currentValue);
lblValue.Refresh(); //Call Refresh to make the label update itself
}
}
static class Program
{
private static ProgressForm progressForm = null;
[STAThread]
static void Main()
{
//Application.EnableVisualStyles();
//Application.SetCompatibleTextRenderingDefault(false);
//Application.Run(new Form1());
progressForm = new ProgressForm();
progressForm.Show();
doWork();
progressForm.Close();
}
//arbitrary example of processing that takes some period of time
static void doWork()
{
int i = 0;
while (i < 100000)
{
progressForm.ChangeValue(i);
Thread.Sleep(1);
i++;
}
return;
}
}
You may use the following instead as you are trying to access UI control other than main thread (from which it is created).
while ( true )
{
Invoke ( ( Action ) (() =>
{
label.Text = val.ToString();
label.Refresh()
Application.DoEvents();
}));
Thread.Sleep( 100 );
}
I recommend you to use "backgroundworker".
First add CheckForIllegalCrossThreadCalls = false; to initialization part otherwise InvalidOperationException occurs.
private void btnDoIt_Click(object sender, EventArgs e)
{
backgroundWorker.RunWorkerAsync();
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
Foo();
}
int total = 0;
private void Foo()
{
for (int i = 0; i <= 100000; i++)
{
total += i;
this.Text = i.ToString();
}
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Run next process
}

Update progress bar in another form while task is running

**Ultimately I am going to have four tasks running concurrently and have another form that contains four progress bars. I would like for each progress bar to update as it's work task is completing.
Here's what I'm trying to do for starters.
I have a form that has some buttons on it. When I click one I'm creating a new task to do some work.
public partial class MyMainForm : Form
{
private void btn_doWork_Click(object sender, EventArgs e)
{
Task task = new Task(RunComparisons);
task.Start();
}
private void RunComparisons()
{
int progressBarValue = 0;
MyProgressBarForm pBar = new MyProgressBarForm(maxValue, "some text");
pBar.ShowDialog();
foreach(string s in nodeCollection)
{
//do some work here
progressBarValue++;
pBar.updateProgressBar(progressBarValue, "some new text");
}
pBar.BeginInvoke(new Action(() => pBar.Close()));
}
}
In another class that contains a form with a progress bar:
public partial class MyProgressBarForm : Form
{
public MyProgressBarForm(int maxValue, string textToDisplay)
{
InitializeComponent();
MyProgressBarControl.Maximum = maxValue;
myLabel.Text = textToDisplay;
}
public void updateProgressBar(int progress, string updatedTextToDisplay)
{
MyProgressBarForm.BeginInvoke(
new Action(() =>
{
MyProgressBarControl.Value = progress;
myLabel.Text = updatedTextToDisplay;
}));
}
When I click the doWork button the progress bar form displays but doesn't update. It just sits there and hangs. If I comment out the pBar.ShowDialog(); then the progress bar form doesn't display but the work to be done is run to completion perfectly.
I had this working perfectly when I was creating my own threads but I read about Tasks and now I'm trying to get this to run that way. Where did I go wrong?
The TPL adds the IProgress interface for updating the UI with the progress of a long running non-UI operation.
All you need to do is create a Progress instance in your UI with instructions on how to update it with progress, and then pass it to your worker which can report progress through it.
public partial class MyMainForm : System.Windows.Forms.Form
{
private async void btn_doWork_Click(object sender, EventArgs e)
{
MyProgressBarForm progressForm = new MyProgressBarForm();
progressForm.Show();
Progress<string> progress = new Progress<string>();
progress.ProgressChanged += (_, text) =>
progressForm.updateProgressBar(text);
await Task.Run(() => RunComparisons(progress));
progressForm.Close();
}
private void RunComparisons(IProgress<string> progress)
{
foreach (var s in nodeCollection)
{
Process(s);
progress.Report("hello world");
}
}
}
public partial class MyProgressBarForm : System.Windows.Forms.Form
{
public void updateProgressBar(string updatedTextToDisplay)
{
MyProgressBarControl.Value++;
myLabel.Text = updatedTextToDisplay;
}
}
This lets the Progress Form handle displaying progress to the UI, the working code to only handle doing the work, the main form to simply create the progress form, start the work, and close the form when done, and it leaves all of the work of keeping track of progress and marhsaling through the UI thread to Progress. It also avoids having multiple UI thread; your current approach of creating and manipulating UI components from non-UI threads creates a number of problems that complicates the code and makes it harder to maintain.
Create your progress bar form on the main UI thread of the parent form, then call the Show() method on the object in your button click event.
Here's an example with 2 bars:
//In parent form ...
private MyProgressBarForm progressBarForm = new MyProgressBarForm();
private void button1_Click(object sender, EventArgs e)
{
progressBarForm.Show();
Task task = new Task(RunComparisons);
task.Start();
}
private void RunComparisons()
{
for (int i = 1; i < 100; i++)
{
System.Threading.Thread.Sleep(50);
progressBarForm.UpdateProgressBar(1, i);
}
}
//In MyProgressBarForm ...
public void UpdateProgressBar(int index, int value)
{
this.Invoke((MethodInvoker) delegate{
if (index == 1)
{
progressBar1.Value = value;
}
else
{
progressBar2.Value = value;
}
});
}
.ShowDialog is a blocking call; execution won't continue until the dialog returns a result. You should probably look in to a BackgroundWorker to process the work on another thread and update the dialog.

Thread safe form manipulation between two forms (WinForms C#)

I have two forms, the main form and one that pops up as a modal dialog. From a process spawned in the main form, I want to dynamically update the text on the modal dialog. Here's what I have:
In the main form, I do this:
// show the wait modal
var modal = new WaitDialog { Owner = this };
// thread the packaging
var thread = new Thread(() => Packager.PackageUpdates(clients, version, modal));
thread.Start();
// hopefully it worked ...
if (modal.ShowDialog() != DialogResult.OK)
{
throw new Exception("Something failed, miserably.");
}
The PackageUpdates method takes the modal dialog, and does this:
// quick update and sleep for a sec ...
modal.SetWaitLabelText("Downloading update package...");
Thread.Sleep(2000);
modal.SetWaitLabelText("Re-packaging update...");
To be thread safe, I do this in the modal dialog:
public void SetWaitLabelText(string text)
{
if (lblWaitMessage.InvokeRequired)
{
Invoke(new Action<string>(SetWaitLabelText), text);
}
else
{
lblWaitMessage.Text = text;
}
}
Everything works great ... most of the time. Every three or four times that the modal pops up, I get an exception on the lblWaitMessage.Text = text; and it's not invoking the command.
Am I missing something in this setup?
Like #Hans Passant pointed out, you should wait for the modal.Load-event. One good option is to use the ManualResetEvent to inform your thread to wait until that happens.
The WaitOne method will block the thread until the Set method is called. Here's a very simple setup which should do the trick.
public partial class Form1 : Form
{
ManualResetEvent m_ResetEvent;
public Form1()
{
InitializeComponent();
m_ResetEvent = new ManualResetEvent(false);
}
private void button1_Click(object sender, EventArgs e)
{
Dialog d = new Dialog { Owner = this, ResetEvent = m_ResetEvent };
var thread = new Thread(new ParameterizedThreadStart(DoSomething));
thread.Start(d);
if (d.ShowDialog() != System.Windows.Forms.DialogResult.OK)
{
throw new Exception("Something terrible happened");
}
}
private void DoSomething(object modal)
{
Dialog d = (Dialog)modal;
// Block the thread!
m_ResetEvent.WaitOne();
for (int i = 0; i < 1000; i++)
{
d.SetWaitLabelText(i.ToString());
Thread.Sleep(1000);
}
}
}
And here is the modal form
public partial class Dialog : Form
{
public Form Owner { get; set; }
public ManualResetEvent ResetEvent { get; set; }
public Dialog()
{
InitializeComponent();
}
public void SetWaitLabelText(string text)
{
if (label1.InvokeRequired)
{
Invoke(new Action<string>(SetWaitLabelText), text);
}
else
{
label1.Text = text;
}
}
private void Dialog_Load(object sender, EventArgs e)
{
// Set the event, thus unblocking the other thread
ResetEvent.Set();
}
}
I think you should rewrite the code to let thread.Start() isn't called before modal.ShowDialog().
As a workaround, you can try this:
public void SetWaitLabelText(string text) {
Invoke(new Action<string>(SetWaitLabelText2), text);
}
void SetWaitLabelText2(string text) {
lblWaitMessage.Text = text;
}
The first method always uses Invoke, regardless the value of InvokeRequired. The second method actually does the thing. This pattern is usable when you always call the function from another thread.

Question on threading in C#

I have a Windows Forms application at the moment, and I want to create a new thread and run a method on another class that accepts an input.
For example
public partial class Form1: Form {
SerialPort serialInput;
// I want to create a new thread that will pass the parameter serialInput into the method
// SMSListener on another class and run the method contionously on the background.
}
class SMS
{
public void SMSListener(SerialPort serial1)
{
serial1.DataReceived += port_DataRecieved;
}
private void port_DataRecieved(object sender, SerialDataReceivedEventArgs e)
{
// Other codes
}
}
How do I perform this in C#? I have seen numerous examples on the web, and most of them run the method on the same class with no parameters, but none that suits my requirements.
Perhaps a Background Worker could help you?
It is a bit hard to understand what you are aiming at.
public class Runner
{
private readonly BackgroundWorker _worker = new BackgroundWorker();
public Runner()
{
_worker.DoWork += WorkerDoWork;
}
public void RunMe(int payload)
{
_worker.RunWorkerAsync(payload);
}
static void WorkerDoWork(object sender, DoWorkEventArgs e)
{
var worker = sender as BackgroundWorker;
while (true)
{
if (worker.CancellationPending)
{
e.Cancel = true;
break;
}
// Work
System.Threading.Thread.Sleep((int)e.Argument);
}
}
}
I am not an expert on Multithreading but to the best of my knowledge you can only start threads on methods that accept an object parameter and return void. So in order to achieve that for your problem (don't shoot me down if there is a better approach!) I would do something like
public partial class Form1: Form {
SerialPort serialInput;
// I want to create a new thread that will pass the parameter serialInput into the method
// SMSListener on another class and run the method contionously on the background.
SMS sms = new SMS();
Thread t = new Thread(sms.SMSListenerUntyped);
t.Start(serialInput);
}
class SMS
{
public void SMSListenerUntyped(object serial1) {
if (serial1 is SerialPort) //Check if the parameter is correctly typed.
this.SMSListener(serial1 as SerialPort);
else
throw new ArgumentException();
}
public void SMSListener(SerialPort serial1)
{
serial1.DataReceived += port_DataRecieved;
}
private void port_DataRecieved(object sender, SerialDataReceivedEventArgs e)
{
// Other code.
}
How about just use the ThreadPool directly with a anonymous method allowing you to access your surrounding locals?
public void OnButtonClick(object sender, EventArgs e)
{
SerialPort serialInput = this.SerialInput;
System.Threading.ThreadPool.QueueUserWorkItem(delegate
{
SmsListener listener = new SmsListener(serialInput);
});
}

Categories