UWP ProgressBar not fully updating from a For loop - c#

I am trying to update a ProgressBar from a For loop, in my head, should be easy but I'm struggling a bit.
I've got a Dispatcher method which only seems to fire off twice. Once after the first loop, then not until it has completed. I.e. the loop count should be 6, so each increment would be 16.666667. The ProgressBar will update once to 16.666667 then will wait until the loop has complete to reach 100.
I've read through a few different questions on here, but can't find the answer. I'm still relatively new to this, so any help explained in the dumbest way possible would be appreciated :)
double progressIncrease = (double)100 / TodaysFixtures.Count;
ProcessingBar.Value = 0;
for (int fixture = 0; fixture < TodaysFixtures.Count; fixture++)
{
// ...
await UpdateProcessingBarAsync(progressIncrease);
}
private async Task UpdateProcessingBarAsync(double progressIncrease)
{
await Window.Current.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
ProcessingBar.Value += progressIncrease;
});
}

Related

C# wpf, how to update a progressbar over 1 second

I cannot for the life of me figure out how to have a progress bar "progress" (fill up) over 1 second.
I do NOT mean one update every one second, what I want is:
After 100 milliseconds, 10% progress
after 200ms, 20% progress
...
after 1000ms, 100% progress.
How does one do this?
await Application.Current.Dispatcher.InvokeAsync(() =>
{
for (int i = 10; i <= 100; i += 10)
{
yourprogressbar.Value = i;
Thread.Sleep(100);
}
});
You need to add async keyword on your method to be able to use await.

Multithreading with Semaphore | App not responding

First about my goal:
I am importing a table with about 1000-5000 rows to a DataTable. This one is bound to a DataGridView. Now for every row there has to run a process that takes about 5-10 seconds. After a single process finished I want to write back the result to the DataTabel (result-column).
Because this process is independent I want to use multithreading to speed it up.
This is an example structure of my current code:
// Will be created for each row
public class FooObject
{
public int RowIndex;
public string Name;
//...
}
// Limiting running tasks to 50
private Semaphore semaphore = new Semaphore(50, 50);
// The DataTable is set up at start-up of the App (columns etc)
private DataTable DtData { get; set; } = new DataTable();
// The button that starts the process
private void btnStartLongRun(object sender, EventArgs e)
{
// some init-stuff
StartRun();
}
private async void StartRun()
{
for (int rowIndex = 0; rowIndex < DtData.Rows.Count)
{
// Creating a task to not block the UI
// Using semaphore here to not create objects
// for all lines before they get in use.
// Having this inside the real task it consumed
// a lot of ram (> 1GB)
await Task.Factory.StartNew(() =>
{
semaphore.WaitOne();
});
// The row to process
var currentRow = DtData.Rows[rowIndex];
// Creating an object from the row-data
FooObject foo = new FooObject()
{
RowIndex = rowIndex;
Name = currentRow["Name"].ToString();
}
// Not awaiting because I want multiple threads
// to run at the same time. The semaphore is
// handling this
TaskScheduler scheduler = TaskScheduler.Current;
Task.Factory.StartNew(() =>
{
// Per-row process
return ProcessFoo(foo);
}).ContinueWith((result) =>
{
FinishProcessFoo(result.Result);
}, CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, scheduler);
}
}
private FooObject ProcessFoo(FooObject foo)
{
// the actual big process per line
}
private void FinishProcessFoo(FooObject foo)
{
// Locking here because I got broken index errors without
lock(DtGrid.Rows.SyncRoot)
{
// Getting the row that got processed
var procRow = DtData.Rows[foo.RowIndex];
// Writing the result to that row
procRow["Result"] = foo.Result;
// Raising the progressbar
pbData.Value++;
}
// Letting the next task start.
semaphore.Release();
}
The big problem:
In the beginning everything is working fine. All threads are running smooth and doing their job. But as longer the app runs, as more it is getting unresponsive. It looks like the app is slowly starting to block more and more.
I started a test-run with 5000 rows. It got in stuck at around row 2000. Sometimes even an error raises that the app isn't responding.
I haven't got a lot experience in multi threading. So maybe this code is totally bad. I appreciate every help in here. I would also be happy about pointing me into another direction to get this running better.
Thank you very much.
Edit
If there is anything I can debug to help in here just tell me.
Edit 2
I already enabled all Common Language Runtime Exceptions to check if there is anything that's not raising an error. Nothing.
If you want to process up to 50 rows in parallel, you could consider using a Parallel.For with a MaxDegreeOfParallelism of 50:
Parallel.For(0, DtData.Rows.Count, new ParallelOptions() { MaxDegreeOfParallelism = 50 }, rowIndex =>
{
//...
});
Starting a new task just to call WaitOne on a Semaphore is a waste of time.
You are using the UI thread to coordinate thousands of async tasks. This is bad. Wrap your call to StartRun in a new task to avoid this.
The better way of doing this is to divide the number of rows by the number of processors, then start one task per processor for just those rows. No need for Semaphore then.

Sequentially call asynchronous methods in a loop

I'm working with some code, that has an approach I've never dealt with before, and am hoping someone might be able to provide a spark of knowledge:
Effectively the class is setup like this:
void Loop()
{
for (int i = 0; i < 100; i++)
{
//need to block calls to this until UpdateComplete is hit
Update(i);
}
}
void Update(int i)
{
//asynchronous - returns immediately
}
void UpdateComplete()//called from another thread
{
//unblock further calls to Update();
}
The big caveat is that it's not possible to call Update() before UpdateComplete() has 'returned' or been calledback.
Ignoring any UI side effects, is there a neat solution to this problem?
I've currently got two strategies - one hacky and one I feel is over-complicated:
1 - Hacky: Set a global class boolean IsBlocked, that Update() sets to true and UpdateComplete() sets to false, and inside my for loop (in Loop()), just put in a while (IsBlocked){}.
2 - Over-complicated: Get rid of the loop altogether. Call Update(0); from inside Loop() instead, and have UpdateComplete() call Update(1);, Update(2); and so on.
I guess I'm hoping for something like a Thread.Pause that can then be remotely 'resumed' by another thread (the separate one that calls UpdateComplete()
Any ideas appreciated!
Async/await can also be used here
TaskCompletionSource<object> tcs = null;
async void Loop()
{
for (int i = 0; i < 100; i++)
{
tcs = new TaskCompletionSource<object>();
Update(i);
await tcs.Task;
}
}
void Update(int i)
{
//asynchronous - returns immediately
}
void UpdateComplete()//called from another thread
{
tcs.TrySetResult(null);
//unblock further calls to Update();
}
Post the updates into the thread pool using QueueUserWorkItem
Have a counter of the number of posted items.
Have main thread wait on AutoResetEvent
When Each update finishes decrement the count under lock.
When the count hits 0 Set the AutoResetEvent to awake the main thread
I didn't post any code cos I assumed you can google how to do each bit, if not comment and I can add code

Display number as it is found

I have simple task in my app, but i have problem.
For example:
for (int i = 1; i <= 1000000; i++)
{
if (i % 2 == 0) {
TextBlock.Text += string.Format("{0},", i);
}
}
While app doing that task it takes long time, so I would like it displays number when it is found and not at the end, all numbers together.
What is the simplest way to do that for newbie like me?
It is WP8, C#.
Thanks a lot.
you can keep adding these numbers in a queue object and have a thread looking for a change in the queue object and simultaneously update the textbox.
So the problem is, that UI doesn't get refreshed until your loop end, even if you are appending text to TextBox. The easy way to fix it - is to add Application.DoEvents() call after TextBlock.Text += string.Format("{0},", i);.
But it has some critical downsides (explained here https://stackoverflow.com/a/5183623/2152334).
The right way is to do calculation in a different thread (using Tasks for example) and update UI thread using Dispatcher:
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
TextBlock.Text += ...
});
You can't refresh the display if your code blocks the UI thread. You need to leave the UI thread for your long task.
// We go to a non-UI thread
TaskEx.Run(() => {
for (int i = 1; i <= 1000000; i++)
{
// some long operation here... -_-"
if (i % 2 == 0)
{
// We return to UI thread after long operation to display the result
Deployment.Current.Dispatcher.InvokeAsync(() => {
TextBlock.Text += string.Format("{0},", i);
});
}
}
});
Also you might consider adding a cancellation token to break the loop in case the user leaves the page being displayed.
[edit: this solution is meant for your comment "my app will display few numbers but calculation takes long time". If you really need 1000000 results, try to Dispatcher.InvokeAsync(...) and TextBlock.Text += ... multiple results at a time.]

CancellationTokenSource cancelling more then expected

Basically i am running a programm that executes a method that is repeated several times (for example 7 times), but i need an option to cancel the CURRENTLY run one of it, but still allow new ones to start.
My issue is that the programm ends as soon as i hit the cancel button. So i seem to be missing something.
Main:
for(int i = 0; i < 7; i++)
{
cts = new CancellationTokenSource();
taskUbung = Task.Factory.StartNew(async() =>
{
await doSomething();
}, cts.Token);
}
Button:
private void buttonSkipUbung_Click(object sender, RoutedEventArgs e)
{
cts.Cancel();
}
So if i am hitting the button on the 3rd round of doSomething(), the remaining 4 will never execute.
What do i have to change for my desired behavior? I could just use some booleans and return in doSomething(), but i figured this would be cleaner and more responsive, if it works that is.
You need to keep track of the tokens you are creating in some kind of list. Otherwise you only remember the last one and call cancel only on that one

Categories