This is a code sample created on OP's request:
First, let's make a thread wrapper. Later on, you can add some random algorithm, input numeric ranges in it. For now, it's important to make a skeleton: generate some number and fire an event with new value; also, it can be started/paused/resumed/aborted. The thread itself is hidden for a user. Note the use of event handle — this is the way to use long-living thread and keep it in a wait state without wasting any CPU time. On a wait of the handle, a thread is switched out and never scheduled back to execution until waken up by the call to
ManualResetEvent.Set
, timeout or
Abort
called in any other thread.
The thread is abstracted from the UI. It's responsibility of the UI thread to add an event handle to the invocation list of event
NumberGenerated
.
[EDIT]
The thread wrapper is very important to avoid parametrized thread start and for better encapsulation in general.
Please see my past answers for more detail:
How to pass ref parameter to the thread[
^],
change paramters of thread (producer) after it started[
^].
This is a VB.NET translation:
Passing arguments to a threaded LongRunningProcess[
^].
VSNetVbHarry was so nice to do this work.
[END EDIT]
namespace RandomTest {
using System.Threading;
internal class ThreadWrapper {
internal class NumberGeneratedEventArgs : System.EventArgs {
internal NumberGeneratedEventArgs(int number) { this.fNumber = number; }
internal int Number { get { return fNumber; } }
int fNumber;
}
internal ThreadWrapper(int sleepTime) {
this.thread = new Thread(this.Body);
this.sleepTime = sleepTime;
}
internal void Start() { this.thread.Start(); }
internal void Abort() { this.thread.Abort(); }
internal void Pause() { this.waitHandle.Reset(); }
internal void Resume() { this.waitHandle.Set(); }
internal event System.EventHandler<NumberGeneratedEventArgs>
NumberGenerated;
void Body() {
int value = 0;
while (true) {
waitHandle.WaitOne();
if (NumberGenerated != null)
NumberGenerated.Invoke(this, new NumberGeneratedEventArgs(value));
value++;
Thread.Sleep(sleepTime);
}
}
Thread thread;
int sleepTime;
ManualResetEvent waitHandle = new ManualResetEvent(false);
}
}
Now, let's use it in the form. I intentionally made it all in one file and avoided use of Designer, so all code would be in one place:
namespace RandomTest {
using System.Windows.Forms;
public partial class FormMain : Form {
const string ButtonStop = "&Stop";
const string ButtonStart = "&Start";
const int SleepTime = 500;
delegate void NumberAction(Label label, int value);
public FormMain() {
Padding = new Padding(10);
Button button = new Button();
button.Text = ButtonStart;
button.Dock = DockStyle.Bottom;
Controls.Add(button);
Label output = new Label();
output.Dock = DockStyle.Fill;
output.AutoSize = false;
Controls.Add(output);
button.Click += delegate(object sender, System.EventArgs eventArgs) {
if (running) {
button.Text = ButtonStart;
wrapper.Pause();
} else {
button.Text = ButtonStop;
wrapper.Resume();
}
running = !running;
};
wrapper.NumberGenerated += delegate(object sender, ThreadWrapper.NumberGeneratedEventArgs eventArgs) {
output.Invoke(new NumberAction(delegate(Label label, int value) {
label.Text = value.ToString();
}), output, eventArgs.Number);
};
wrapper.Start();
this.Closing += delegate(object sender, System.ComponentModel.CancelEventArgs e) {
wrapper.Abort();
};
}
bool running;
ThreadWrapper wrapper = new ThreadWrapper(SleepTime);
}
}
I also tried to use the syntax compatible with C# v.2, which is still used. I also hope it will be easier if you still want to translate it in VB.NET. In later versions, lambda syntax of anonymous delegates is highly beneficial.
As to VB.NET, please see my comment to the question.
This code is fully tested. Again, sorry this is not VB.NET.
—SA