MaxDegreeOfParallelism blocks the Main thread? - c#

I have the next code, and works fine:
private void BtBasicIntroClick(object sender, EventArgs e)
{
var stopwatch = new Stopwatch();
stopwatch.Reset();
stopwatch.Start();
var executionDataflowBlockOptions = new ExecutionDataflowBlockOptions
{
//TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext(),
MaxDegreeOfParallelism = 1
};
var actionBlock = new ActionBlock<int>(s =>
{
//comboBox1.Items.Add((s*3).ToString());
Invoke(new Action(() => comboBox1.Items.Add((s * 3).ToString())));
}, executionDataflowBlockOptions);
var numeros = Enumerable.Range(0, 40000);
foreach (var numero in numeros)
{
actionBlock.Post(numero);
}
Task.Factory.StartNew(() =>
{
actionBlock.Completion.ContinueWith(delegate
{
stopwatch.Stop();
if (InvokeRequired)
{
Invoke(new Action(() =>
label1.Text = stopwatch.ElapsedMilliseconds.ToString(CultureInfo.InvariantCulture)));
}
});
actionBlock.Complete();
actionBlock.Completion.Wait();
});
}
The Windows Forms works fine... the proccess doesn't block the UI
But if I change the MaxDgreeOfParallelism to other value (2 or 3 or 4...) the UI is blocked until the proccess finish.
I've see the Parallel Tasks window and the Thread Window in Visual Studio and in both cases everything works in Worked Threads, but in the latter case (When the MaxDgreeOfParallelism it's different from 1 ) the UI don't respond until the proccess finish
Why?

When I try your code, the UI is blocked the whole time, even with MaxDegreeOfParallelism = 1. That's because all your block does is to call Invoke(), which blocks the UI thread.
It's possible that under some circumstances, one thread calling Invoke() over and over won't be enough to block the UI thread completely, but 2 threads almost certainly will.
So, what you're trying to do doesn't make any sense. You're not going to gain anything from using dataflow, or anything similar.
What you should do to fix this is not to have thousands of items in the UI. No human is going to go though such huge list anyway.

Related

WPF strange behavior with thread adn invoke to update progress bar

I have a method that reads from database and return result items one by one using yield return. I call it in foreach loop and at each iteration it invokes to STA thread for update ProgressBar. In this case I get about 6 seconds for all for some params. But if I remove Invoke, then I get 28 seconds for the same params. I test this behavior in separate application and can say that with Invoke each iterations to main thread processing in 10 times slower. However, Invoke is allmost at 6 times faster (not slower!) in my application than in other completed examples.
Any suggestions?
In general it looks like this:
Thread thread = new Thread(() =>
{
int itemNumber = 0;
foreach (object item in SelectItems())
{
itemNumber++;
// doing some staff ...
Application.Current.Dispatcher.Invoke(
new Action(() =>
{
ProgressBar1.Value = itemNumber * 100 / count;
}),
null);
}
});
thread.IsBackground = true;
thread.Start();
Update
I think I need to say that is big application and there is about half hundred background threads. And I don't think they all can work at same time.

Why using Task.ContinueWith hurts my program's responsiveness?

We have a video player written in WPF with a scroll bar. When the scroll bar is dragged right-left the CurrentFrameTime is updated and triggers UpdateFrames which, in turn, grabs the frame and shows it. That works fine.
But, sometimes grabbing the frame can take time (because of the disk for example) and though CurrentFrameTime value can be already changed, the UpdateFrames can be "stuck" and still be waiting for previous frame in ...GetAsync().Result.
What is decided to do is to move Dispatcher.BeginInvoke into ContinueWith block. Now, each time the CurrentFrameTime is changed, the previous operation will be canceled(we don't need to show the frame if frame time was already changed) and an up-to-date frame should be shown. But, for some reason because of this change the application became slower. When I drag the scroll it can take a few seconds before the image is updated.
What could happened that moving the code into ContinueWith has slowed down the video player?
MainApplication without ContinueWith
_threadUpdateUI = new Thread(new ThreadStart(UpdateFrames));
public long CurrentFrameTime
{
get{...}
set
{
...
_fetchFrame.Set();
}
}
void UpdateFrames()
{
while(run)
{
_fetchFrame.WaitOne();
var frame = Cache.Default.GetAsync(CurrentFrameTime)
.Result;
Dispatcher.BeginInvoke(new Action(() => ShowFrame(frame.Time, frame.Image)));
}
}
Cache
public Task<VideoFrame> GetAsync(long frameTime)
{
//this i used when cache is disabled
if (GrabSynchronously)
{
var tcs = new TaskCompletionSource<VideoFrame>();
//reading from file
var frame2 = FrameProvider.Instance.GetFrame(frameTime);
tcs.SetResult(frame2);
return tcs.Task;
}
...
}
MainApplication WITH ContinueWith
void ShowFrames()
{
while(run)
{
_fetchFrame.WaitOne();
_previousFrameCancellationToken.Cancel();
_previousFrameCancellationToken = new CancellationTokenSource();
Cache.Default.GetAsync(CurrentFrameTime).ContinueWith((task) =>
{
var frameTime = task.Result.Time;
var frameImage = task.Result.Image
Dispatcher.BeginInvoke(new Action(() => ShowFrame(frameTime, frameImage)));
}, _previousFrameCancellationToken.Token);
}
}
df
In your old way your UpdateFrames loop would block every .Result call. This made your loop self metering, only allowing one request "in flight" at a time even if _fetchFrame got .Set() called many times while it was waiting for .Result to finish.
In your new way every call to _fetchFrame.Set() triggers another task to start up and be "in flight" (assuming GrabSynchronously is false) even if it never gets used. This is flooding your system with requests and is causing your slowdown.
One possible solution is to put another semaphore of some type to limit the number of concurrent requests for frames you can handle.
Semaphore _frameIsProcessing = new Semaphore(5, 5); //Allows for up to 5 frames to be requested at once before it starts blocking requests.
private void ShowFrames()
{
while (run)
{
_fetchFrame.WaitOne();
_previousFrameCancellationToken.Cancel();
_previousFrameCancellationToken = new CancellationTokenSource();
_frameIsProcessing.WaitOne();
Cache.Default.GetAsync(CurrentFrameTime).ContinueWith((task) =>
{
_frameIsProcessing.Release();
if(_previousFrameCancellationToken.IsCancellationRequested)
return;
var frameTime = task.Result.Time;
var frameImage = task.Result.Image;
Dispatcher.BeginInvoke(new Action(() => ShowFrame(frameTime, frameImage)));
});
}
}

Waiting for Parallel.For

I know that there is no need to wait for a Parallel.For but since UI Windows messages (WinForms message pump) are being processed during the Parallel.For I would like to wait for Parallel.For to finish.
Is it a good idea to encapsulate a Parallel.For inside a Task, then wait for it? Is there any better approach?
Thanks.
CancellationTokenSource token = new CancellationTokenSource();
const int len = 100;
double[] array = new double[len];
Task t = Task.Factory.StartNew(delegate {
Parallel.For(0, len, delegate(int i, ParallelLoopState loopState) {
array[i] += 1;
});
try
{
t.Wait(token.Token);
}
catch (OperationCanceledException e)
{
rc = false;
}
Instead of Parallel.For why not use just Task and call Task.WaitAll()?
var t1 = Task.Run(() => {});
var t2 = Task.Run(() => {});
Task.WaitAll(t1, t2);
If you want to wait for a Parallel.For() to finish, you simply don't start it in a separate task!
Parallel.For() doesn't return until it has completed (even if it uses multiple threads to do the work).
Note that Parallel.For() returns a ParallelLoopResult - it does not return a task or anything else which would allow you to wait for it to finish, so if it did return before it was complete (as you are asserting) there would be no way to know when it had finished.
What makes you be so confident that WM_PAINT and other Windows messages are pumped while Parallel.For or Task.Wait is blocking on the UI thread?
The following simple example proves you're wrong. The form is not getting repainted to red for the whole 15 seconds, while Parallel.For is working.
using System;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WinFroms_21681229
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
this.Shown += MainForm_Shown;
}
void MainForm_Shown(object sender, EventArgs e)
{
MessageBox.Show("Before");
this.BackColor = Color.FromName("red");
this.Invalidate();
// the form is not getting red for another 15 seconds
var array = new double[] { 1, 2, 3 };
Parallel.For(0, array.Length, (i) =>
{
System.Threading.Thread.Sleep(5000);
Debug.Print("data: " + array[i]);
});
MessageBox.Show("After");
}
}
}
Here is how to run the tasks in parallel, while keeping the UI responsive:
async void MainForm_Shown(object sender, EventArgs e)
{
MessageBox.Show("Before");
this.BackColor = Color.FromName("red");
this.Invalidate();
// the form is not getting red for another 15 seconds
var array = new double[] { 1, 2, 4 };
var tasks = array.Select((n) => Task.Run(()=>
{
Thread.Sleep(5000);
Debug.Print("data: " + n);
}));
await Task.WhenAll(tasks);
MessageBox.Show("After");
}
You could have done something like await Task.Factory.StartNew(() => Parallel.For(...)), but that would use at least one more thread than really needed.
To understand what's going on behind the scene here, you'd need to understand how WinForms message loop works inside Application.Run, how await yields the execution control back to the message loop and then gets continued via WindowsFormsSynchronizationContext, when the task has completed. The async-await tag wiki could help, it contains links to some great, must-read resources.
If you're interested to know how message pumping works during blocking operations, check this answer.

Task.WaitAll() not working as expected

I'm trying to figure out how to work with the Task class. In the past I have always used the regular Thread class, but I'm trying to grasp all of the asynchronous programming...
As an example, I created a main Winforms application that has all the code.
The relevant code for my problem is:
//Relevant delegates
public delegate void MethodAction(int num);
public delegate void MethodConversion();
public delegate void OnCompletionAction(string completiontext);
//Button user presses
private void button4_Click(object sender, EventArgs e)
{
richTextBox1.Clear();
sw.Reset();
sw.Start();
Sync.RunAsync3(calcSim);
}
//The method that simulates a calculation by adding a sleep
//the input param threadlength is just to allow threads to take longer than others
//since I'm multithreading, I have to invoke the writing code on the windows RichTextbox control
private void calcSim(int threadlength)
{
string threadname = Thread.CurrentThread.Name;
for (int i = 0; i < 10; i++) //Thread calc should take 3s
{
Thread.Sleep(300 + threadlength);
richTextBox1.Invoke((MethodConversion)(() =>
{
richTextBox1.AppendText(string.Format("Thread: {0}\tVersion: {1}\n", threadname, (i + 1).ToString()));
}));
}
}
//Class that contains the different processing methods
public static class Sync
{
public static event OnCompletionAction OnProcCompletion;
public static void RunAsync3(MethodAction doM)
{
Task[] t = new Task[4];
for(int i = 0; i < 4; i++)
{
t[i] = Task.Factory.StartNew((Action)(() => { doM(50 * i); }));
}
Task.WaitAll(t);
if (OnProcCompletion != null) OnProcCompletion("RunSync method finished");
}
}
The problem lies within Task.WaitAll(t)... For some reason, which I can't figure out, it completely blocks on that line and doesn't respond anymore. If I omit that line, the form gets updated in realtime and the execution take about 3 seconds.
My question is: why isn't Task.WaitAll() blocking the UI thread for 3 seconds before releasing it and allowing the rest of the code to execute?
I know it should be blocking the UI for some time (until all threads are calculated), but it blocks the complete app endlessly. It seems to be waiting forever?
EDIT
I've been suggested to use WhenAll instead of WaitAll. I have rewritten RunAsync3 as follows:
public static void RunAsync3(MethodAction doM)
{
Task[] t = new Task[4];
for(int i = 0; i < 4; i++)
{
t[i] = Task.Factory.StartNew((Action)(() => { doM(50 * i); }));
}
//Task.WaitAll(t); -> deadlock
Task.WaitAll(new Task [] { Task.WhenAll(t) });
if (OnProcCompletion != null) OnProcCompletion("RunSync method finished");
}
But this is still getting deadlocked...? I might be using the WhenAll incorrectly?
EDIT 2
Because everybody claiming that I was blocking the UI thread were right, I decided to try this another way: by running a new thread as my calling thread inside the UI thread (so that blocking now would occur on my thread instead of UI thread). This works, but is obviously not the best way to do this!
private void button4_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ThreadStart(() =>
{
richTextBox1.Invoke((MethodConversion)(() => richTextBox1.Clear()));
sw.Reset();
sw.Start();
Sync.RunAsync3(calcSim);
}));
t.Start();
}
public static void RunAsync3(MethodAction doM)
{
Task[] t = new Task[4];
for(int i = 0; i < 4; i++)
{
t[i] = Task.Factory.StartNew((Action)(() => { doM(50 * i); }));
}
Task.WaitAll(t);
//Task.WaitAll(new Task [] { Task.WhenAll(t) });
if (OnProcCompletion != null) OnProcCompletion("RunSync method finished");
}
You're causing a deadlock.
The UI thread is waiting for 4 tasks to be completed.
On the other hand, those 4 tasks, running calcSim are trying to invoke code on the UI thread -> Deadlock.
You should be using Task.WhenAll() instead. That method will return a new task that will be marked as completed when all your for tasks have completed. If you await that task, your UI thread will be freed, and so calcSim will be able to invoke code on the UI thread, avoiding a deadlock.
Update
You're using it wrong. You're still using WaitAll, which is a blocking call. You should replace it with WhenAll.
await Task.WhenAll(t);
From the documentation:
Creates a task that will complete when all of the supplied tasks have
completed.
By calling await on the result, your UI thread will be free - until all 4 tasks complete. When that happens, your RunAsync3 method will resume.
Task.WaitAll blocks and waits for all task to complete and you are calling it on the UI thread.
All your task are trying to call richTextBox1.Invoke (in the UI thread) but your UI thread is blocked in Task.WaitAll. Deadlock.
Because it waits as your threads finish. They run exactly 3 seconds 300X10

Parallel.ForEach freezing on last loop [duplicate]

More newbie questions:
This code grabs a number of proxies from the list in the main window (I couldn't figure out how to make variables be available between different functions) and does a check on each one (simple httpwebrequest) and then adds them to a list called finishedProxies.
For some reason when I press the start button, the whole program hangs up. I was under the impression that Parallel creates separate threads for each action leaving the UI thread alone so that it's responsive?
private void start_Click(object sender, RoutedEventArgs e)
{
// Populate a list of proxies
List<string> proxies = new List<string>();
List<string> finishedProxies = new List<string>();
foreach (string proxy in proxiesList.Items)
{
proxies.Add(proxy);
}
Parallel.ForEach<string>(proxies, (i) =>
{
string checkResult;
checkResult = checkProxy(i);
finishedProxies.Add(checkResult);
// update ui
/*
status.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Normal,
new Action(
delegate()
{
status.Content = "hello" + checkResult;
}
)); */
// update ui finished
//Console.WriteLine("[{0}] F({1}) = {2}", Thread.CurrentThread.Name, i, CalculateFibonacciNumber(i));
});
}
I've tried using the code that's commented out to make changes to the UI inside the Parallel.Foreach and it makes the program freeze after the start button is pressed. It's worked for me before but I used Thread class.
How can I update the UI from inside the Parallel.Foreach and how do I make Parallel.Foreach work so that it doesn't make the UI freeze up while it's working?
Here's the whole code.
You must not start the parallel processing in your UI thread. See the example under the "Avoid Executing Parallel Loops on the UI Thread" header in this page.
Update: Or, you can simply create a new thread manuall and start the processing inside that as I see you have done. There's nothing wrong with that too.
Also, as Jim Mischel points out, you are accessing the lists from multiple threads at the same time, so there are race conditions there. Either substitute ConcurrentBag for List, or wrap the lists inside a lock statement each time you access them.
A good way to circumvent the problems of not being able to write to the UI thread when using Parallel statements is to use the Task Factory and delegates, see the following code, I used this to iterate over a series of files in a directory, and process them in a Parallel.ForEach loop, after each file is processed the UI thread is signaled and updated:
var files = GetFiles(directoryToScan);
tokenSource = new CancellationTokenSource();
CancellationToken ct = tokenSource.Token;
Task task = Task.Factory.StartNew(delegate
{
// Were we already canceled?
ct.ThrowIfCancellationRequested();
Parallel.ForEach(files, currentFile =>
{
// Poll on this property if you have to do
// other cleanup before throwing.
if (ct.IsCancellationRequested)
{
// Clean up here, then...
ct.ThrowIfCancellationRequested();
}
ProcessFile(directoryToScan, currentFile, directoryToOutput);
// Update calling thread's UI
BeginInvoke((Action)(() =>
{
WriteProgress(currentFile);
}));
});
}, tokenSource.Token); // Pass same token to StartNew.
task.ContinueWith((t) =>
BeginInvoke((Action)(() =>
{
SignalCompletion(sw);
}))
);
And the methods that do the actual UI changes:
void WriteProgress(string fileName)
{
progressBar.Visible = true;
lblResizeProgressAmount.Visible = true;
lblResizeProgress.Visible = true;
progressBar.Value += 1;
Interlocked.Increment(ref counter);
lblResizeProgressAmount.Text = counter.ToString();
ListViewItem lvi = new ListViewItem(fileName);
listView1.Items.Add(lvi);
listView1.FullRowSelect = true;
}
private void SignalCompletion(Stopwatch sw)
{
sw.Stop();
if (tokenSource.IsCancellationRequested)
{
InitializeFields();
lblFinished.Visible = true;
lblFinished.Text = String.Format("Processing was cancelled after {0}", sw.Elapsed.ToString());
}
else
{
lblFinished.Visible = true;
if (counter > 0)
{
lblFinished.Text = String.Format("Resized {0} images in {1}", counter, sw.Elapsed.ToString());
}
else
{
lblFinished.Text = "Nothing to resize";
}
}
}
Hope this helps!
If anyone's curious, I kinda figured it out but I'm not sure if that's good programming or any way to deal with the issue.
I created a new thread like so:
Thread t = new Thread(do_checks);
t.Start();
and put away all of the parallel stuff inside of do_checks().
Seems to be doing okay.
One problem with your code is that you're calling FinishedProxies.Add from multiple threads concurrently. That's going to cause a problem because List<T> isn't thread-safe. You'll need to protect it with a lock or some other synchronization primitive, or use a concurrent collection.
Whether that causes the UI lockup, I don't know. Without more information, it's hard to say. If the proxies list is very long and checkProxy doesn't take long to execute, then your tasks will all queue up behind that Invoke call. That's going to cause a whole bunch of pending UI updates. That will lock up the UI because the UI thread is busy servicing those queued requests.
This is what I think might be happening in your code-base.
Normal Scenario: You click on button. Do not use Parallel.Foreach loop. Use Dispatcher class and push the code to run on separate thread in background. Once the background thread is done processing, it will invoke the main UI thread for updating the UI. In this scenario, the background thread(invoked via Dispatcher) knows about the main UI thread, which it needs to callback. Or simply said the main UI thread has its own identity.
Using Parallel.Foreach loop: Once you invoke Paralle.Foreach loop, the framework uses the threadpool thread. ThreadPool threads are chosen randomly and the executing code should never make any assumption on the identity of the chosen thread. In the original code its very much possible that dispatcher thread invoked via Parallel.Foreach loop is not able to figure out the thread which it is associated with. When you use explicit thread, then it works fine because the explicit thread has its own identity which can be relied upon by the executing code.
Ideally if your main concern is all about keeping UI responsive, then you should first use the Dispatcher class to push the code in background thread and then in there use what ever logic you want to speedup the overall execution.
if you want to use parallel foreach in GUI control like button click etc
then put parallel foreach in Task.Factory.StartNew
like
private void start_Click(object sender, EventArgs e)
{
await Task.Factory.StartNew(() =>
Parallel.ForEach(YourArrayList, (ArraySingleValue) =>
{
Console.WriteLine("your background process code goes here for:"+ArraySingleValue);
})
);
}//func end
it will resolve freeze/stuck or hang issue

Categories

Resources