so this code takes value of incremented numbers stream with random starting number. If start1 value bigger then start2 I want display corresponding line with textbox, else another line.
Problem is I can't stop program until the given number of cycles is not satisfied. Button just hangs during implantation. I understand that the reason why this happens is a loop. Now I'm trying stop it with backgroundWorker, but I got same result, cancel button hangs same way.
Code which I want use with backgroundWorker located inside backgroundWorker1_ProgressChanged. I don't really quite understand what is happening here. Maybe you help me to figure out what I'm doing wrong:
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
namespace XX_8_0
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;
}
private void startAsyncButton_Click(object sender, EventArgs e)
{
if (backgroundWorker1.IsBusy != true)
{
backgroundWorker1.RunWorkerAsync();
}
}
private void cancelAsyncButton_Click(object sender, EventArgs e)
{
if (backgroundWorker1.WorkerSupportsCancellation == true)
{
backgroundWorker1.CancelAsync();
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 1; i <= 10; i++)
{
if (worker.CancellationPending == true)
{
e.Cancel = true;
break;
}
else
{
System.Threading.Thread.Sleep(500);
worker.ReportProgress(i * 10);
}
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
var random = new Random();
var start1 = random.Next(0, 100);
var start2 = random.Next(0, 100);
var incrementor1 = start1 > 50 ? -1 : 1;
var incrementor2 = start2 > 50 ? -1 : 1;
var cV1 = start1;
var cV2 = start2;
for (var i = 0; i < 1000; i++)
{
if (cV1 == 101) incrementor1 = -1;
if (cV1 == 0) incrementor1 = 1;
if (cV2 == 101) incrementor2 = -1;
if (cV2 == 0) incrementor2 = 1;
if (cV1 > cV2)
{
textBox1.AppendText("ID: (" + i + ") CV1: (1): [" + cV1 + "] CV2: (0) [" + cV2 + "]\n");
}
else
{
textBox1.AppendText("ID: (" + i + ") CV1: (0): [" + cV1 + "] CV2: (1) [" + cV2 + "]\n");
}
cV1 += incrementor1;
cV2 += incrementor2;
}
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled == true)
{
resultLabel.Text = "Canceled!";
}
else if (e.Error != null)
{
resultLabel.Text = "Error: " + e.Error.Message;
}
else
{
resultLabel.Text = "Done!";
}
}
}
}
Note: I think your example is perhaps not really a good example of what BackgroundWorker can do. Essentially your code just sleeps a bit then reports progress, hardly useful in another thread.
Your backgroundWorker1_ProgressChanged is quite heavy with adding 1000 textBox1.AppendText() and thus could take some time.
Button just hangs during implantation. I understand that the reason why this happens is a loop.
When you consider backgroundWorker1_ProgressChanged executes on the UI thread, no matter what number of clicks you make on cancel won't be processed until backgroundWorker1_ProgressChanged returns. All UI processing has to be done by the UI thread. It should be pointed out that the background worker thread is also suspended during this time and unfortunately it is the only part of the code that tests for cancellation. Even if ReportProgress were asynchonous, you still require the UI thread to process the cancel button click event and mark the work as CancellationPending.
I suggest you don't report as much in one go in backgroundWorker1_ProgressChanged or perhaps consider reporting adding items to the textBox1 as batches.
That way you won’t flood your message pump and your application will be more responsive.
Change your backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) to use batches as:
var builder = new StringBuilder();
for (var i = 0; i < 1000; i++)
{
if (cV1 == 101) incrementor1 = -1;
if (cV1 == 0) incrementor1 = 1;
if (cV2 == 101) incrementor2 = -1;
if (cV2 == 0) incrementor2 = 1;
if (cV1 > cV2)
{
builder.Append("ID: (" + i + ") CV1: (1): [" + cV1 + "] CV2: (0) [" + cV2 + "]\n");
}
else
{
builder.Append("ID: (" + i + ") CV1: (0): [" + cV1 + "] CV2: (1) [" + cV2 + "]\n");
}
cV1 += incrementor1;
cV2 += incrementor2;
}
textbox1.AppendText(builder.ToString());
Since your ProgressChanged handler is very busy updating the UI about 1000 times per ReportProgress call, it will use all UI thread CPU time and that thread will never be able to process Cancel button since it is already way too busy.
Given that you use a BackgroundWorker, you should really move most of ProgressChanged in the DoWork handler and remove that sleep in the background thread.
The way, you use the BackgroundWorkerdoes not make any sense. The UI thread should do as little as possible and the background thread should as much as possible. And it does not make sense to sleep in the background thread in your case as the background work does not depend on time elapsed.
Once the code has been moved, you need to send the data back to the UI thread. But this is very easy since there in an overload of ReportProgress that allow to pass any .NET object to the UI thread (assuming the background worker is created from the UI thread which is generally the case). On the other side, you simple cast the user data to the appropriate type and use that information to append appropriate text to the text box.
With improved code, you should be able to process that data much faster. By the way a StringBuilder should also be used to build the string. In fact, it should have a dramatic impact on UI performance since you update the UI text 1000 time less often.
Related
I work on console version of link tester.
I start the function on multiple threads but I can't cancel them by key pressing.
Have an idea how I can do that ?
try
{
Thread[] tr = new Thread[Variables.Threads];
int i = 0;
while (i < Variables.Threads && Variables.running)
{
tr[i] = new Thread(new ThreadStart(Program.Runner));
i++;
}
//Start each thread
foreach (Thread x in tr)
{
x.Start();
}
//Console.ReadKey();
Task.Factory.StartNew(() =>
{
while (Colorful.Console.ReadKey().Key != ConsoleKey.Escape);
Variables.running = false;
foreach (Thread x in tr)
{
x.Abort();
}
Program.Menu();
});
}
catch (Exception)
{
}
EDIT : When my threads are a near of end, all of my app don't move after
The console is a not a good environment to start learning multitasking, much less multithreading. My advise to learning Multitasking - and especially Multithreading - is the BackgroundWorker in a WindowsForms applciation. The event queue does the main thing of keeping your application alive, without blocking I/O. And while the BackgroundWorker is horribly dated and should be removed from production code, it is pretty good training wheels.
Also your current code is swallowing exceptions, including fatal ones. This is a cardinal sin of exception handling. Do not do that.
At the end of the day, you can only do cancelation checking and progress reporting between distinct lines of code. In this BGW example, I had the advantage that I had to write all the loops anyway - so deep cancelation checking and progress reporting was in the cards. But if you use "somebody elses code", chances are high you have to wait for one function call to return before you can report and check.
#region Primenumbers
private void btnPrimStart_Click(object sender, EventArgs e)
{
if (!bgwPrim.IsBusy)
{
//Prepare ProgressBar and Textbox
int temp = (int)nudPrim.Value;
pgbPrim.Maximum = temp;
tbPrim.Text = "";
//Start processing
bgwPrim.RunWorkerAsync(temp);
}
}
private void btnPrimCancel_Click(object sender, EventArgs e)
{
if (bgwPrim.IsBusy)
{
bgwPrim.CancelAsync();
}
}
private void bgwPrim_DoWork(object sender, DoWorkEventArgs e)
{
int highestToCheck = (int)e.Argument;
//Get a reference to the BackgroundWorker running this code
//for Progress Updates and Cancelation checking
BackgroundWorker thisWorker = (BackgroundWorker)sender;
//Create the list that stores the results and is returned by DoWork
List<int> Primes = new List<int>();
//Check all uneven numbers between 1 and whatever the user choose as upper limit
for(int PrimeCandidate=1; PrimeCandidate < highestToCheck; PrimeCandidate+=2)
{
//Report progress
thisWorker.ReportProgress(PrimeCandidate);
bool isNoPrime = false;
//Check if the Cancelation was requested during the last loop
if (thisWorker.CancellationPending)
{
//Tell the Backgroundworker you are canceling and exit the for-loop
e.Cancel = true;
break;
}
//Determin if this is a Prime Number
for (int j = 3; j < PrimeCandidate && !isNoPrime; j += 2)
{
if (PrimeCandidate % j == 0)
isNoPrime = true;
}
if (!isNoPrime)
Primes.Add(PrimeCandidate);
}
//Tell the progress bar you are finished
thisWorker.ReportProgress(highestToCheck);
//Save Return Value
e.Result = Primes.ToArray();
}
private void bgwPrim_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
pgbPrim.Value = e.ProgressPercentage;
}
private void bgwPrim_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
pgbPrim.Value = pgbPrim.Maximum;
this.Refresh();
if (!e.Cancelled && e.Error == null)
{
//Show the Result
int[] Primes = (int[])e.Result;
StringBuilder sbOutput = new StringBuilder();
foreach (int Prim in Primes)
{
sbOutput.Append(Prim.ToString() + Environment.NewLine);
}
tbPrim.Text = sbOutput.ToString();
}
else
{
tbPrim.Text = "Operation canceled by user or Exception";
}
}
#endregion
However in your case, Multithreading seems unesseary. Multitasking without Threads would propably be better. Threads only help if you got a CPU bound task. And "checking links" sounds like a Network bound task. Threads have some extra headaches that you are better off avoiding.
I am filling Richtextbox with adding lines from another thread that extract links from web. when the web urls links increase and go more than 9000 it hangs the UI and take long time don't know why ! , using button click event to fire this method ( thread )
Using AppendText(Environment.NewLine) method to fill the richtextbox
Here is the snippet code of my work :
if (URLLMemoRichTxt.Lines.Length == 0)
{
XtraMessageBox.Show("You have to get some links first");
return;
}
var thd = new Thread(() =>
{
if (this.InvokeRequired)
{
if (URLLMemoRichTxt.InvokeRequired)
{
URLLMemoRichTxt.Invoke((MethodInvoker)delegate ()
{
foreach (string line in URLLMemoRichTxt.Lines)
{
if (!GetEmailsListArraylist.Contains(line) && line.Trim() != string.Empty)
{
if (LinksToGetEmailsRichTxt.InvokeRequired)
{
LinksToGetEmailsRichTxt.Invoke((MethodInvoker)delegate ()
{
GetEmailsListArraylist.Add(line);
// LinksToGetEmailsRichTxt.Text += line + "\n";
LinksToGetEmailsRichTxt.AppendText(Environment.NewLine + line);
LinksToGetEmailsLabel.Text = LinksToGetEmailsRichTxt.Lines.Length.ToString();
});
}
else
{
GetEmailsListArraylist.Add(line);
// LinksToGetEmailsRichTxt.Text += line + "\n";
LinksToGetEmailsRichTxt.AppendText(Environment.NewLine + line);
LinksToGetEmailsLabel.Text = LinksToGetEmailsRichTxt.Lines.Length.ToString();
}
}
}
});
}
else
{
foreach (string line in URLLMemoRichTxt.Lines)
{
if (!GetEmailsListArraylist.Contains(line) && line.Trim() != string.Empty)
{
GetEmailsListArraylist.Add(line);
// LinksToGetEmailsRichTxt.Text += line + "\n";
LinksToGetEmailsRichTxt.AppendText(Environment.NewLine + line);
LinksToGetEmailsLabel.Text = LinksToGetEmailsRichTxt.Lines.Length.ToString();
}
}
if (MainTabcontrol.InvokeRequired)
{
MainTabcontrol.Invoke((MethodInvoker)delegate ()
{
MainTabcontrol.SelectedTabPageIndex = 1;
});
}
else
{
MainTabcontrol.SelectedTabPageIndex = 1;
}
}
}
else
{
}
if (MainTabcontrol.InvokeRequired)
{
MainTabcontrol.Invoke((MethodInvoker)delegate ()
{
MainTabcontrol.SelectedTabPageIndex = 1;
});
}
else
{
MainTabcontrol.SelectedTabPageIndex = 1;
}
});
thd.SetApartmentState(ApartmentState.MTA);
thd.Start();
using button click event to fire this method ( thread )
Here is your code, boiled down:
private void button1_Click(object sender, EventArgs e)
{
new Thread(() =>
{
if (URLLMemoRichTxt.InvokeRequired)
{
URLLMemoRichTxt.Invoke((MethodInvoker)delegate ()
{
// .. do some "work" in here ...
Thread.Sleep(9000);
});
}
}).Start();
}
Let's analyze what's happening here:
User clicks the button.
Running in the UI thread, the Click event creates a New Thread and starts it.
Inside that new thread, immediately ask if we are running in a different thread than the one that owns the RichTextBox with InvokeRequired (the answer is, of course, yes!).
Tell the application to run some code on the thread that owns the RichTextBox with Invoke().
Running in the UI thread again, do the actual "work".
End Result?
All your "work" is run in the main UI thread as if no threading ever occurred.
Why create a thread and then tell it to run its work back on the UI thread?
How should it be done correctly?
Here's a slightly more robust version of the boiled down code, with only the part that updates the UI within the Invoke() block:
private void button1_Click(object sender, EventArgs e)
{
new Thread(() =>
{
// do some "work" in our new thread
for(int i = 1; i <= 10; i++)
{
Thread.Sleep(1000); // the "work"
// when you need to UPDATE THE UI, now is the time for Invoke()
// note that only the part that is updating the UI is within this block
// the rest of the code/loop is still running in the new thread
URLLMemoRichTxt.Invoke((MethodInvoker)delegate ()
{
URLLMemoRichTxt.AppendText("Line " + i.ToString() + "\r\n");
});
// ... possibly more work in the new thread ...
}
}).Start();
}
With this approach, the UI will remain responsive while the for loop runs and the work is done. Additionally, the RichTextBox will happily update with the new entries without blocking.
I need to input specific keys (arrows.left and arrows.right) in my console application without blocking a loop.
Here's the code:
while (fuel>0) {
moveAndGenerate();
for (int i=0;i<road.GetLength(0); i++)
{
for (int j = 0; j < road.GetLength(1); j++)
{
Console.Write(string.Format("{0} ", road[i, j]));
}
Console.Write(Environment.NewLine + Environment.NewLine);
}
Console.WriteLine("Paliwo: "+ (fuel=fuel-5) + "%");
moveAndGenerate();
replaceArrays();
Thread.Sleep(1000);
Console.Clear();
}
it generates a simple game:
| :x|
| : |
|x: |
| :↑|
Within the loop as long as there's fuel. I want the arrow to move right/left without waiting for Console.ReadKey(). Is it possible?
As RB stated, you can setup a listener for the key press instead, and check if true if so they you reset the key press to null and move the car in that direction
Listen for key press in .NET console app
Another possible workaround is to use a BackgroundWorker to listen for input. This way you can handle both user input and the main code at the "same" time. It's similar to a separate thread.
You'll need to add using System.ComponentModel; to your program.
static BackgroundWorker backgroundWorker1 = new BackgroundWorker(); // Create the background worker
static string input = ""; // where the user command is stored
public static void Main()
{
// All the code preceding the main while loop is here
// Variable declarations etc.
//Setup a background worker
backgroundWorker1.DoWork += BackgroundWorker1_DoWork; // This tells the worker what to do once it starts working
backgroundWorker1.RunWorkerCompleted += BackgroundWorker1_RunWorkerCompleted; // This tells the worker what to do once its task is completed
backgroundWorker1.RunWorkerAsync(); // This starts the background worker
// Your main loop
while (fuel>0)
{
moveAndGenerate();
for (int i=0;i<road.GetLength(0); i++)
{
for (int j = 0; j < road.GetLength(1); j++)
{
Console.Write(string.Format("{0} ", road[i, j]));
}
Console.Write(Environment.NewLine + Environment.NewLine);
}
Console.WriteLine("Paliwo: "+ (fuel=fuel-5) + "%");
moveAndGenerate();
replaceArrays();
Thread.Sleep(1000);
Console.Clear();
}
// This is what the background worker will do in the background
private static void BackgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
if (Console.KeyAvailable == false)
{
System.Threading.Thread.Sleep(100); // prevent the thread from eating too much CPU time
}
else
{
input = Console.In.ReadLine();
// Do stuff with input here or, since you can make it a static
// variable, do stuff with it in the while loop.
}
}
// This is what will happen when the worker completes reading
// a user input; since its task was completed, it will need to restart
private static void BackgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs
{
if(!backgroundWorker1.IsBusy)
{
backgroundWorker1.RunWorkerAsync(); // restart the worker
}
}
}
I've been trying to make a program to split larger text files into smaller pieces so they are easier to work with.
I currently have two issues and cannot figure out what is going on.
Issue 1: The background worker will sometimes fire multiple times. I cannot seem to figure out why or how many times it decides to run. It will run the split and on the final file it seems like it loops back to the beginning of do work and run it again. It fires off multiple do work completed tasks as well. To complicate things if I set the number of files split to a different number I can get a different number of times the background worker seems to fire, but it doesn't directly correlate to the number of files. Sometimes the same number of files causes the background worker to fire only once and sometimes it fires multiple times.
Issue 2: Sometimes the split will not create all the files. With some files if I run it it will create the first two files and then drop the rest. Seems to only happen when I set the number to 3 files to split into. if I take the line count and add it up it should equal out correctly. So i'm not sure what is going on there.
Calling Thread
private void StartSplit()
{
if (int.TryParse(NumberOfFilesTB.Text, out _numberOfFiles))
{
if (bg.IsBusy)
{
((MainWindow)Application.Current.MainWindow).SetStatus("Warning",
"Please only run one split process at a time.");
return;
}
((MainWindow)Application.Current.MainWindow).DisplayAlert(
"Split is running, you will receive an alert when it has finished. You may use other tools while the split is running.");
var args = new List<string> { _filepath, _includeHeaders.ToString(), _numberOfFiles.ToString() };
bg.DoWork += bg_DoWork;
bg.WorkerReportsProgress = true;
bg.ProgressChanged += ProgressChanged;
bg.RunWorkerCompleted += bg_RunWorkerCompleted;
bg.WorkerSupportsCancellation = true;
bg.RunWorkerAsync(args);
ProcessText.Text = "Running split process";
}
else
{
((MainWindow)Application.Current.MainWindow).SetStatus("Warning", "Please enter a number for number of files");
}
}
Background Thread
private void bg_DoWork(object sender, DoWorkEventArgs e)
{
var args = e.Argument as List<string>;
string filepath = args[0];
string includeHeaders = args[1];
int numberOfFiles = Convert.ToInt32(args[2]);
int numberOfRows = _lineCount / numberOfFiles;
_tempath = Path.GetDirectoryName(_filepath);
Directory.CreateDirectory(_tempath+"\\split");
if (includeHeaders == "True")
{
using (var reader = new StreamReader(File.OpenRead(filepath)))
{
_lines.Clear();
_header = reader.ReadLine();
_lines.Add(_header);
for (int i = 0; i < _lineCount; i++)
{
if (bg.CancellationPending)
{
e.Cancel = true;
break;
}
int percentage = (i + 1) * 100 / _lineCount;
bg.ReportProgress(percentage);
_lines.Add(reader.ReadLine());
if (i % numberOfRows == 0)
{
_counter++;
Debug.WriteLine(i);
if (i == 0)
{
//skip first iteration
_counter = 0;
continue;
}
_output = _tempath + "\\" + "split\\" + _fileNoExt + "_split-" + _counter + _fileExt;
_filesMade.Add(_output);
File.WriteAllLines(_output, _lines.ConvertAll(Convert.ToString));
_lines.Clear();
_lines.Add(_header);
}
}
}
}
else
{
using (var reader = new StreamReader(File.OpenRead(filepath)))
{
_lines.Clear();
_header = reader.ReadLine();
_lines.Add(_header);
for (int i = 0; i < _lineCount; i++)
{
if (bg.CancellationPending)
{
e.Cancel = true;
break;
}
int percentage = (i + 1) * 100 / _lineCount;
bg.ReportProgress(percentage);
_lines.Add(reader.ReadLine());
if (i % numberOfRows == 0)
{
_counter++;
if (i == 0)
{
//skip first iteration
_counter = 0;
continue;
}
string output = _tempath + "\\" + "split\\" + _fileNoExt + "_split-" + _counter + _fileExt;
_filesMade.Add(_output);
File.WriteAllLines(output, _lines.ConvertAll(Convert.ToString));
_lines.Clear();
}
}
}
}
}
Run Worker Completed
private void bg_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
StopSplit();
_filesMade.Clear();
ProcessText.Text = "Split cancelled";
return;
}
_filesMade.Clear();
ProcessText.Text = "Split has completed, click here to open the directory";
}
I bet your BgW is a member of your class...
In Startsplit() you add a new callback each time this function is executed.
That's why it runs multiple times.
Other answer after dinner.
Finished dinner...
your method of counting is faulty in multiple ways:
1) If you are missing files, I bet it's the last one. E.g. 30 lines, 3 files:
i % numberOfRows is zero at i=0, 10, 20, but i does not reach 30.
2) you are missing lines, e.g. 31 lines 4 files:
Files are saved at i=7, 14, 21, 28. Lines 29-31 are missing.
I suggest you use a nested for loop, outer one for files, inner one for lines, and improve your calculation. And put all your lists and counters inside the function!
I hope you appreciate my answer. I hate typing on a tablet. But also didn't want to start my computer just for this... ;-)
I already have this but it doesn't work... I need to check if a bit from plc is high or not. If it is still high I need to wait 5 seconds and check again.. Now I am trying to find something so there is some visual feedback for the user. In a textbox I want to put 'waiting...' while the points after waiting increase per 5 seconds. I tried a lot of things but can't seem to get it to work. Mostly of the time's it just hangs 25 seconds without updating the GUI and then it continues... :/
// First check if the plc bit, that says if there is still an order active,
// is still set. If so then we wait a couple seconds.
var bitorder = Main.PIOGetValue(jbmis.IO_GET_INPUT, "BoxManInStarted");
int counter = 1;
string loadingpoints = "";
loadtimer.Tick += timer_Tick;
loadtimer.Interval = (int)TimeSpan.FromSeconds(5).TotalMilliseconds;
loadtimer.Start();
loadtimer.Enabled = true;
// Stopwatch sw = new Stopwatch();
while(bitorder != 0 && loadtimercounter != 25)
{
// TODO multithreaded
#region testcode
// MessageBox.Show("Waiting for previous order to be stopped" + loadingpoints);
// Context.UserMessageService
// .ShowMessage("Waiting for previous order to be stopped" +
// loadingpoints, "Waitingfororder");
// sw.Start();
// while (sw.Elapsed < TimeSpan.FromSeconds(25))
// {
// if (sw.Elapsed.Seconds % 5 == 0)
// {
// loadingpoints = loadingpoints + ".";
// tbScannedEANPick.Background = Brushes.OrangeRed;
// tbScannedEANPick.Text = "Waiting" + loadingpoints;
// }
// }
// sw.Stop();
// loadingpoints = loadingpoints + ".";
// tbScannedEANPick.Background = Brushes.OrangeRed;
// tbScannedEANPick.Text = "Waiting" + loadingpoints;
// tbScannedEANPick.UpdateLayout();
#endregion
if (loadtimercounter % 5 == 0)
{
loadingpoints = loadingpoints + ".";
tbScannedEANPick.Background = Brushes.OrangeRed;
tbScannedEANPick.Text = "Waiting" + loadingpoints;
tbScannedEANPick.IsReadOnly = true;
bitorder = Main.PIOGetValue(jbmis.IO_GET_INPUT, "BoxManInStarted");
}
counter ++;
}
// After 25 seconds stop timer and continue
loadtimer.Stop();
void timer_Tick(object sender, EventArgs e)
{
loadtimercounter += 5;
}
I am searching for half a day... I tried to use Thread.sleep, timer, stopwatch, ... all in main thread or side thread..
Thanks in advance!!
You need to do the work on a separate thread. Look into asynchronous programming
Or you could just simply use multi threading. I would recommend using asynchronous programming for both doing the background work and updating the GUI's textbox control.
You should use a background worker.
There is a dedicated report progress event that can be used to update the loading box that you need.
Background Worker Class and example