Objective
I'm trying to build a progress tracking mechanism which supports "branching".
Abstract
The principal concept here is that at every stage I know the current index and count of steps till this stage is complete.
Sample Scenario
var tasks = new Task[]
{
new Task(5), // 5 sub tasks
new Task(2), // 2 sub tasks
};
Assuming these items are run serially, progress percentage would look as follows:
0%
10%
20%
30%
40%
50%
50%
75%
100%
100%
Question Time!
First of all, what are the correct terms for what I'm doing? I have been unable to properly search because of this.
Secondly, what are the mathematics of what I'm doing? What are the variables that I have to track?
Final Product
I would like to end up with an interface that looks like this:
class TaskManagerExample
{
void Execute(Task[] tasks)
{
var pm = new ProgressManager(); // <- this class does the magic
// v-- and this is how the magic is used :)
pm.ProgressChanged += (object sender, EventArgs e) =>
{
progressBar1.Value = (sender as ProgressManager).TotalProgress;
};
pm.SetTotal(tasks.Length);
foreach (var task in tasks)
{
task.Execute(pm.Branch());
pm.NextStep();
}
}
}
class Task
{
protected int SubTasks = 0;
public Task(int subTasks){ this.SubTasks = subTasks; }
public void Execute(ProgressManager pmb)
{
pmb.SetTotal(this.SubTasks);
for(int i = 0; i < this.SubTasks; i++)
{
// do something
pmb.NextStep();
}
}
}
My problem is the implementation for ProgressManager. I've been able to achieve a parent-child relation so Branch() and NextStep() are ok. I'm mostly confused about how TotalProgress should be implemented.
How would one go through the tree to calculate the right progress position?
Related
Forgive me if this is simple. I come from the Embedded RToS world and have learnt c# for just for the purpose of test equipment for production. I think I have solved all the obvious issues already, just the tasks are not acting as expected.
I have a test jig with 10 serial ports and other things attached and a very basic windows forms UI with 10 list box's that need to all run simultaneously. I have cut the code down to just what is needed for the demo of the problem (no serial ports etc)
I am unable to get 10 listbox's (or textboxs) to update from 10 tasks properly. I can easily do one at a time, even 5 works, but 10 and it does not work.
I was getting exception out of range as it somehow gets passed a 10, but I only pass 0-9 so I reduced the task count to 9 for now until I understand the fundamental issue(s).
private void buttonStart_Click(object sender, EventArgs e)
{
Task.Factory.StartNew(() => PTT.ProductionTest());
}
This is the task from the PTT class, it will manage and monitor the 10 tasks so the UI is still responsive
public static void ProductionTest()
{
//Somewhere to store the tasks
List<Task> TestSlotTasks = new List<Task>();
//Create the tasks for each of the test slots
for (int i = 0; i != 9; ++i)
{
TestSlotTasks.Add(Task.Factory.StartNew(() => new PTT().TestSlot(i)));
Program.ListBoxAddLine(string.Format("CALL[{0}]: task start/stop test", i), System.Drawing.Color.LawnGreen, i);
}
Task.WaitAll(TestSlotTasks.ToArray());
}
This is supposed to be a non static program so I can create multiple instances (so I can run it many times without issue, that is my intention anyway, right?)
private void TestSlot(int slot)
{
for (var z = 0; z != 10; ++z)
{
Program.ListBoxAddLine(string.Format("PTT[{0}]: task start/stop test", slot), System.Drawing.Color.LawnGreen, slot);
}
}
This is the invoke method i use to update the listBox. The list box is in a generic list something like this
//ListBoxs.Add(listBoxSlot1); //Done for each of the 10 list box's to put in a list
//This is the method that will add a line to the main text box and to the log file
public static void ListBoxAddLine(string TextToAdd, System.Drawing.Color textColor, int slot)
{
//Update the list box on the main screen
if (mainForm != null)
{
if (FormPTT.ListBoxs[slot].InvokeRequired)
{
FormPTT.ListBoxs[slot].Invoke((MethodInvoker)delegate ()
{
FormPTT.ListBoxs[slot].ForeColor = textColor;
FormPTT.ListBoxs[slot].TopIndex = FormPTT.ListBoxs[slot].Items.Add(TextToAdd);
});
}
else
{
FormPTT.ListBoxs[slot].ForeColor = textColor;
FormPTT.ListBoxs[slot].TopIndex = FormPTT.ListBoxs[slot].Items.Add(TextToAdd);
}
}
}
The output I get is pretty random if i go above 5 or 6 list box's for this example.
Using the above code, I get this
output in the list box's, ignore the other stuff
So its all ok up until slot 8, its shows the Call[7] task start/stop but nothing from the task call and 9 and 10 are ok, but I dont call it 10 times, so how is 10 getting updated? I dont pass 10? and where is 8's PTT[7]... output?
I assume I am just not understanding a few things about tasks...
I have a WPF project (VS2010, .NET4.0) in which I create a rather big ModelVisual3D object (read from custom format STL file, process info, create mesh, etc.) This takes about 3-4 sec. to be created and another 2-3 sec. to do a mainViewport.Children.Add(ModelVisual3D).
I do this all in a custom class and call this method:
class My3DModel
{
...
public MyModelVisual3D createModelVisual3D(MyTypes tType, int tNumber)
{
this.myModelVisual3D = new MyModelVisual3D(tType, tNumber);
for (int i = 0, j = 0; i < this.Triangles.Length; i++)
{
this.mesh.Positions.Add(this.Triangles[i].Vertex1);
this.mesh.Positions.Add(this.Triangles[i].Vertex2);
this.mesh.Positions.Add(this.Triangles[i].Vertex3);
this.mesh.Normals.Add(this.Triangles[i].Normal);
this.mesh.Normals.Add(this.Triangles[i].Normal);
this.mesh.Normals.Add(this.Triangles[i].Normal);
this.mesh.TriangleIndices.Add(j++);
this.mesh.TriangleIndices.Add(j++);
this.mesh.TriangleIndices.Add(j++);
}
this.model3DGroup.Children.Add(new GeometryModel3D(this.mesh, material));
this.myModelVisual3D.Content = this.model3DGroup;
return this.myModelVisual3D;
}
}
The return value is also a custom class I created:
class ToothModelVisual3D : ModelVisual3D
{
//VARIABLES
private MyTypes myType;
private int number;
//OPERATORS
public MyTypes MyType
{get { return myType; } set { myType = value; }}
public int Number
{get { return number; } set { number = value;}}
public ToothModelVisual3D() { }
public ToothModelVisual3D(MyTypes tType, int tNumber) { MyType = tType; Number = tNumber; }
}
All I want to do is the following once in the beginning of the program:
{
My3DModel myModel;
myModel = new My3DModel();
myModel.readFileBytes("C:\\registered\\" + 1 + ".stl");
myModel.loadTriangles();
mainViewport.Children.Add(myModel.createModelVisual3D(MyTypes.Sometype, 1);
}
If I do it on the main thread the UI hangs. If I do it on a worker thread and invoke mainViewport.Children.Add(...) it says it cannot access the resourses created on that worker thread. Help?!
From what I understand I've reached a point where I have two threads and resources belonging to each of them (mainViewport => UIThread & myModel => WorkerThread). Neither thread can access directly the other's resource but creating and using myModel on the UIThread makes it hang... All I want to do is have enough responsiveness from the UI, so the user may minimize the program while waiting for it to load the models, nothing more. How can I do that? Is there a way to do all the CPU heavy work on the UIThread, so no resource conflicts arise and have a worker thread that only handles UI for that time?
PS: I've tried with Thread, BackgroundWorker & Task<TResult> classes. Results were similar if not to say the same.
PPS: The full version will load massive models which will load more than 30-40 sec...
I recently came across the same issue when porting an XNA application to WPF.
In my case I partially resolved this by using a background thread to load the positions, normals, and indices from file. Then in that same thread, construct a memory stream containing XAML for the Model3DGroup with the GeometryModel3D and MeshGeometry3D.
Then, in the UI thread, once the memory stream is available, load the model...
Model3DGroup model = System.Windows.Markup.XamlReader.Load(memoryStream) as Model3DGroup;
There is still a delay, but as file access is done in a background thread, it is not as severe.
Sorry for the late answer, but I actually managed to workaround the problem long time ago the following way:
delegate void myDelegate();
private void fileOpenButton_Click(object sender, RoutedEventArgs e)
{
try
{
Thread ViewportLoaderThread = new Thread(loadViewportItemsAsync);
ViewportLoaderThread.IsBackground = true;
ViewportLoaderThread.Start();
}
catch (Exception err) { UtilsProgram.writeErrorLog(err.ToString()); }
}
private void loadViewportItemsAsync()
{
try
{
//TRY to browse for a file
if (!browseForFile()) return;
Dispatcher.Invoke(new Action(() => { myStatusBar.Visibility = System.Windows.Visibility.Visible; menuItemHelpDemo.IsEnabled = false; }), null);
//Load file, unpack, decrypt, load STLs and create ModelGroup3D objects
UtilsDen.DenModel = new DenLoader(UtilsDen.Filename, UtilsDen.Certificate, UtilsDen.PrivateKey, this);
//Load the models to viewport async
myDelegate asyncDel = new myDelegate(sendModelsToViewportAsync);
this.Dispatcher.BeginInvoke(asyncDel, null);
}
catch (Exception err) { MessageBox.Show(UtilsProgram.langDict["msg18"]); UtilsProgram.writeErrorLog(err.ToString()); }
}
private void sendModelsToViewportAsync()
{
for (int i = 0; i < UtilsDen.DenModel.StlFilesCount; i++)
{
//Add the models to MAIN VIEWPORT
ModelVisual3D modelVisual = new ModelVisual3D();
GeometryModel3D geometryModel = new GeometryModel3D();
Model3DGroup modelGroup = new Model3DGroup();
geometryModel = new GeometryModel3D(UtilsDen.DenModel.StlModels[i].MeshGeometry, UtilsDen.Material);
modelGroup.Children.Add(geometryModel);
modelVisual.Content = modelGroup;
mainViewport.Children.Add(toothModelVisual);
}
}
The key was to use the this.Dispatcher.BeginInvoke(asyncDel, null); as it works on the main thread, but does not lag it, because it is executed asynchronously.
Using a delegate still appears to introduce a lag on the UI, a better solution is create the model in a worker thread and then freeze it. The model can then be cloned by the UI thread without the annoying exception. This works for me with models which take 25 seconds or more to load. The only issue I've found with this is that it doesn't work if the model contains a texture.
I have a Canvas control with various elements on, in this particular function I am allowing a user to drag the end point of a line around the canvas. In the MouseMove function I call e.GetPosition().
The function is, according to the VS performance analyzer, close to 30% of total CPU for the app when constantly moving around. Its pretty slow. What can I do to increase this performance?
CurrentPoint = e.GetPosition(PointsCanvas);
I've faced the same problem while using MouseMove on windows phone 8. It seems that while dragging , events (containing the coordinates you need ) are raised at regular time interval ( depending on what you do in the implementation in your listeners, every 20 ms for example). So what I did was to populate a Queue with my coordinates and create a Thread that consume that Queue by enqueue the first element and do the logic I want. Like that the logic is not done serially because it's another thread who does the job.
I don't know if I'm enough clear so please take a look to the code below :
//Class used to store e.getPosition(UIElement).X/Y
public class mouseInformation
{
public int x { get; set; }
public int y { get; set; }
public mouseInformation(int x, int y, String functionName)
{
this.x = x;
this.y = y;
}
}
private readonly Queue<mouseInformation> queueOfEvent = new Queue<mouseInformation>();
//MouseMove listener
private void wpCanvas_MouseDragged(object sender, System.Windows.Input.MouseEventArgs e)
{
//Instead of "wpCanvas" put the name of your UIElement (here your canvas name)
mouseInformation mouseDragged = new mouseInformation((int)e.GetPosition(wpCanvas).X, (int)e.GetPosition(wpCanvas).Y);
EnqueueMouseEvent(mouseDragged);
}
//Allow you to add a MouseInformation object in your Queue
public void EnqueueMouseEvent(mouseInformation mi)
{
lock (queueOfEvent)
{
queueOfEvent.Enqueue(mi);
Monitor.PulseAll(queueOfEvent);
}
}
//Logic that your consumer thread will do
void Consume()
{
while (true)
{
mouseInformation MI;
lock (queueOfEvent)
{
while (queueOfEvent.Count == 0) Monitor.Wait(queueOfEvent);
MI = queueOfEvent.Dequeue();
}
// DO YOUR LOGIC HERE
// i.e DoSomething(MI.x, MI.y)
}
}
And don't forget to create the thread in your Main() or in MainPage_Loaded(object sender, RoutedEventArgs e) method if you are Windows phone user.
System.Threading.ThreadStart WatchQueue = new System.Threading.ThreadStart(Consume);
System.Threading.Thread RunWatchQueue = new System.Threading.Thread(WatchQueue);
RunWatchQueue.Name = "Events thread";
RunWatchQueue.Start();
To be simple less you do in your MouseMove listener, more speed it will be.
You can aswell do the logic asynchronously or even use Bresenham algorithm to simulate more events.
Hope it helps.
Are you using any effects such as dropshaddow etc?
I recently had the situation where e.GetPosition() was also using 30% of the app's cpu resources, which doesn't make any sense right?
I turns out that up the visual tree there was a control applying a dropshaddow effect and that was what was slowing everything down so much...
I have this tricky task I've been trying to achieve for quiet sometime but till now I couldn't think of anything to make it work. anyway here is the scenario...
I have a winform application contains a listview and a button.
the listview contains 1 column which holds the data I need to pass to my functions later on. the column contains lets say 50 rows containing a list of links.
Now I have this function which I'm using to fetch and grab the contents of these links (5 links at a time) with parallel multithreaded mode using (Task Parallel Library):
//List<int> currentWorkingItem //contains the indices of the items in listview
//List<string> URLsList //contains the URLs of the items in listview
Parallel.ForEach(URLsList, new ParallelOptions() { MaxDegreeOfParallelism = 5 }, (url, i, j) =>
{
//show to user this link is currently being downloaded by highlighting the item to green...
this.BeginInvoke((Action)(delegate()
{
//current working item
mylistview.Items[currentWorkingItem[(int)j]].BackColor = green;
}));
//here I download the contents of every link in the list...
string HtmlResponse = GetPageResponse(url);
//do further processing....
});
Now the above code works perfectly... but sometimes I want the user to abort certain thread which is currently running and continue with the rest of the threads in the list... is that achievable in this? if so please help me out.. I'd really appreciate any solution or suggestions..
Try using Task library with cancellation tokens. I find it more elegant and safer approach to do your thing. Here is a quote good example of doing that:
using System;
using System.Threading.Tasks;
using System.Threading;
namespace CancelTask
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Press 1 to cancel task");
var cTokenSource = new CancellationTokenSource();
// Create a cancellation token from CancellationTokenSource
var cToken = cTokenSource.Token;
// Create a task and pass the cancellation token
var t1 = Task<int>.Factory.StartNew(()
=> GenerateNumbers(cToken), cToken);
// to register a delegate for a callback when a
// cancellation request is made
cToken.Register(() => cancelNotification());
// If user presses 1, request cancellation.
if (Console.ReadKey().KeyChar == '1')
{
// cancelling task
cTokenSource.Cancel();
}
Console.ReadLine();
}
static int GenerateNumbers(CancellationToken ct)
{
int i;
for (i = 0; i < 10; i++)
{
Console.WriteLine("Method1 - Number: {0}", i);
Thread.Sleep(1000);
// poll the IsCancellationRequested property
// to check if cancellation was requested
if (ct.IsCancellationRequested)
{
break;
}
}
return i;
}
// Notify when task is cancelled
static void cancelNotification()
{
Console.WriteLine("Cancellation request made!!");
}
}
}
Original article could be found here: http://www.dotnetcurry.com/ShowArticle.aspx?ID=493
ok after struggling with this I finally found an efficient and an easy solution for this..
it required me only a hashtable which contains the indicies of the selected items in the listview and a simple bool value. the index is the key and the bool (true, false) is the value. the bool value is like an (on/off) switch indicates that the current loop is aborted or not.. so in order to abort specific thread simple I need to pass the key(the index) of the selected item on my listview to the foreach loop and check if the bool switch is on or off and that's basically it...
so my final code will be like this:
//I declared the hashtable outside the function so I can manage it from different source.
private Hashtable abortingItem;
Now when I click grab button it should fill the hashtable with the selected indicies...
abortingItem = new Hashtable();
for (int i = 0; i < myURLslist.SelectedItems.Count(); i++)
{
//false means don't abort this.. let it run
abortingItem.Add(myURLslist.SelectedItems[i].index, false);
}
//here should be the code of my thread to run the process of grabbing the URLs (the foreach loop)
//..........................
now if I need to abort specific item all I need is to select the item in the listview and click abort button
private void abort_Click(object sender, EventArgs e)
{
if (abortingItem != null)
{
for (int u = 0; u < myURLslist.SelectedIndices.Count; u++)
{
//true means abort this item
abortingItem[myURLslist.SelectedIndices[u]] = true;
}
}
}
In my foreach loop all I need is a simple if else statement to check if the bool is on or off:
//List<int> currentWorkingItem //contains the indices of the items in listview
//List<string> URLsList //contains the URLs of the items in listview
Parallel.ForEach(URLsList, new ParallelOptions() { MaxDegreeOfParallelism = 5 }, (url, i, j) =>
{
//aborting
if (!(bool)abortingItem[currentWorkingItem[(int)j]])
{
//show to user this link is currently being downloaded by highlighting the item to green...
this.BeginInvoke((Action)(delegate()
{
//current working item
mylistview.Items[currentWorkingItem[(int)j]].BackColor = green;
}));
//here I download the contents of every link in the list...
string HtmlResponse = GetPageResponse(url);
//do further processing....
}
else
{
//aborted
}
});
that's simply it..
Below is a method that I want to ship off into a background worker but I am struggling how to do it based on how created my method. As you can it doesn't return anything which is ok but it expects a directoryInfo object everytime it is recalled.
private void getSizeForTargetDirectory(DirectoryInfo dtar)
{
// generate a collection of objects. files comes first and then directories.
foreach (Object item in collection )
{
if (item == file)
{
track the size of the files as you encounter.
}
else if (item == directory)
{
// found a new directory, recall the method. !!!
}
}
}
This is my first time using a background worker so I'm a little stuck, I tried implementing something thanks to the help found here but got stuck when I realised my method was recursive.
How do I display progress during a busy loop?
I implemented a doWork event handler method but noticed that i needed to somehow recall the method if I had more files and folders to process on lower sub levels.
I have a simple button click event handler that calls my 'getSizeForTargetDirectory()' method when the current selected node is a directory.
private void retrieveInfoButton_Click(object sender, EventArgs e)
{
// check to see if the path is valid
// reset the labels and textfields.
string fullPath = treeDrives.SelectedNode.FullPath;
string sNodesName = treeDrives.SelectedNode.Text;
if (directory) // Enter here if its a directory.
{
string parentPath = treeDrives.SelectedNode.Parent.FullPath;
DirectoryInfo[] dirArray = populateFoldersArray(parentPath);
for (int i = 0; i < dirArray.Length; i++)
{
if (dirArray[i].Name == sNodesName)
{
getSizeForTargetDirectory(dirArray[i]);
// do work !
Hopefully that explains what I am trying to do and how I am doing it. Question is how can i use the report progress feature of the background worker class when the bulk of the work I am trying to ship is coming from a recursive method.
Through early testing I noticed that my getSize method was incredibly efficient after a few tweaks and reported size information for the current supplied folder very quickley but then again I use quite a powerful dev machine so this may not be true for all users.
Thanks For Reading, Hope someone can help !!!
I think it is much simpler to use the built-in methods on either Directory or DirectoryInfo to obtain all directories, or files, using the recursive search option:
public partial class Form1 : Form
{
private Action<float> updateProgMethod;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
updateProgMethod = UpdateProgress;
}
private void GetDirectorySizeAsync(string path)
{
backgroundWorker.RunWorkerAsync(path);
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
DirectoryInfo di = new DirectoryInfo((string)e.Argument);
di.GetTotalSize(ProgressCallback);
}
// Takes callbacks from the GetTotalSize() method
private void ProgressCallback(float p)
{
// Invokes update progress bar on GUI thread:
this.BeginInvoke(updateProgMethod, new object[] { p });
}
// Actually updates the progress bar:
private void UpdateProgress(float p)
{
progressBar.Value = (int)(p * (progressBar.Maximum - progressBar.Minimum)) + progressBar.Minimum;
}
}
public static class IOExtensions
{
public static long GetTotalSize(this DirectoryInfo directory, Action<float> progressCallback)
{
FileInfo[] files = directory.GetFiles("*.*", SearchOption.AllDirectories);
long sum = 0;
int countDown = 0;
for (int i = 0; i < files.Length; i++)
{
sum += files[i].Length;
countDown--;
if (progressCallback != null && countDown <= 0)
{
countDown = 100;
progressCallback((float)i / files.Length);
}
}
return sum;
}
}
It's hard to guess progress without knowing the number of files or folders first!
EDIT: I've improved the code a little.
If, when you call a method, you don't know how long the method is going to take or how many discrete steps are going to be involved, then there is no way to display a progress bar while the method is executing.
In my opinion, the purpose of a progress bar is not to give reliable information about when a task is going to be completed. Rather, the purpose is to keep the user from freaking out and cancelling the whole operation because they think your program has locked up and isn't doing anything at all.
Since you're iterating through directories and sub-directories, a simpler approach here might be to just display the current directory in a Label. This would give the user a relaxing sense that things are happening, and if the directories are all ordered alphabetically, they can even gauge for themselves the overall progress of the operation.
I would report how far you have gotten since you don't know the goal until you get there. I would do it once per invocation. Perhaps # of files and # of directories seen so far.