Semaphore in KeyUp event handler -- locks keyboard input - c#

I'm trying to understand Semaphore's.
In short, I've placed a "long" running procedure (which accesses Network resources), InitializeNamesAsync("","",""), in a KeyUp event handler. I'm trying to allow the user to do continuous typing without slowdown while viewNames is being initialized by InitializeNamesAsync(). Since the user will be continuously typing, the KeyUp event handler will be called many times while the InitializeNamesAsync() method is running.
While the below code compiles fine, it locks up forever completely stopping keyboard input.
So my questions are:
is this an appropriate use of Semaphore's?
how can I make this work?
Is there a better way?
TIA
Having defined
ResourceLock = new Semaphore(0, 1);
private async void _cboLastName_KeyUpAsync(object sender, KeyEventArgs e)
{
if (viewNames == null)
{
ResourceLock.WaitOne();
await InitializeNamesAsync("", "", "");
ResourceLock.Release();
}
}

There's fundamental issue with your design, though you are using the Semaphore to allow multiple threads to enter and execute the event inside the critical region, but challenge is, which thread are you blocking?
As the event is executed on the Ui thread, which is just 1 and unique, what's happening is:
Your code enters the Event, Calls the WaitOne for the Semaphore on the Ui Thread and it's done you are blocked, it doesn't even execute the Async method as expected
Check the out the following Console code, what do you think is the result ?
Following code leads to Deadlock, since Ui or Main Console Thread is waiting upon itself
async Task Main()
{
Semaphore s = new Semaphore(0, 2);
for(int x = 0; x < 5;x++)
{
s.WaitOne();
await Test(x);
s.Release();
}
}
async Task Test(int x)
{
$"Entering : {x}".Dump();
await Task.Delay(3000);
}
In above code await Test(x); and s.Release(); are never called
What are the options, review modified design:
async Task Main()
{
for(int x = 0; x < 5;x++)
{
await Test(x);
s.WaitOne();
}
}
Semaphore s = new Semaphore(0,2);
async Task Test(int x)
{
$"Entering : {x}".Dump();
await Task.Delay(3000);
s.Release();
}
What's different here:
Async method was called before the Semaphore WaitOne is called
Semaphore Release happens post the finish of Async method, not on same thread (in this case on threadpool thread)
And you will find this code will execute successfully without any deadlock
What's the solution:
Don't call WaitOne on a unique thread like Ui thread, that's a recipe for deadlock, especially when Release is also scheduled on same thread
Call Release on a separate thread (I have used the Async method, which is using Threadpool thread in this case)
Other Details:
Ideally Semaphore is meant for the multiple threads to enter the critical region, if you are expecting only one thread, then Sempahore may not be the right choice, but it helps signaling threads unlike lock, you may also review ManualResetEvent and AutoResetEvent, which supports Signaling / EventWaitHandle use cases

The thread is blocked because you enter it twice and semaphore doesn't allow enter the same thread twice(while e.g. Monitor.Enter allows - but then it is not clear why would you need it here).
As I understand you need to launch initialization in the background.
Since it is UI thread you might not need to use synchronization primitives(in this case, at least, not in general). I think it would be enough just having two variables like
beingInitialized
and initialized with the code like
private async void EnsureInitialized()
{
if(!initialized && !beingInitialized)
{
beingInitalized = true;
await StartInitialization();
initalized = true;
beingInitialized = false;
}
}
And call it then as fire and forget
like
private async void _cboLastName_KeyUpAsync(object sender, KeyEventArgs e)
{
EnsureInitialized();
...

Related

How do I marshal an event from Task.Run back to the UI thread?

I have a method that is "partially" async, meaning that one code path runs async and the other runs synchronously. I can't currently make the synchronous part async, although I may be able to in the future.
public async Task UpdateSomethingAsync(){
if (ConditionIsMet){
await DoSomethingAsync;
}else{
DoSomethingSynchronous;
}
}
Both DoSomethingAsync and DoSomethingSynchronous are I/O bound. Calling this method from the Winforms UI thread with "await" causes it to block the UI thread if the Synchronous path is taken, which is to be expected.
private async void MyDropDownBox_DropDownClosed(object sender, EventArgs e)
{
//This blocks if the DoSomethingSynchronous path is taken, causing UI to
//become unresponsive.
await UpdateSomethingAsync();
}
So off to Stephen Cleary's blog I go. His suggestion (although for CPU bound code instead of I/O bound) is to run the method with Task.Run, as if it were completely synchronous, while documenting that the method is "partially" async. However, events raised by DoSomethingSynchronous now cause an exception, I believe due to the fact that they are now on a different thread from the UI.
private async void MyDropDownBox_DropDownClosed(object sender, EventArgs e)
{
//This no longer blocks, but events will not marshal back to UI Thread
//causing an exception.
await Task.Run(()=> UpdateSomethingAsync());
}
How can this be fixed?
Don't update the UI, or any model bound to the UI inside of UpdateSomethingAsync or any of the methods that it calls. Create a class that will hold the data required to update your UI, and return an instance of that class from UpdateSomethingAsync.
DoSomethingAsync will return a Task<ThatClassYouCreated> and DoSomethingSynchronous just returns an instance of ThatClassYouCreated. Then, back in MyDropDownBox_DropDownClosed after you await UpdateSomethingAsync, use the instance returned by UpdateSomethingAsync to update your UI or your model.
public class UpdatedInformation
{
public int UpdateId { get; set; }
public string UpdatedName { get; set; }
public DateTimeOffset Stamp { get; set; }
// etc, etc...
}
public class YourForm : Form
{
private async Task<UpdatedInformation> DoSomethingAsync()
{
var result = new UpdatedInformation();
// Something is awaited...
// Populate the properties of result.
// Do not modify your UI controls. Do not modify the model bound to those controls.
return result;
}
private UpdatedInformation DoSomethingSynchronous()
{
var result UpdatedInformation();
// Populate the properties of result.
// Do not modify your UI controls. Do not modify the model bound to those controls.
return result;
}
private async Task<UpdatedInformation> UpdateSomethingAsync()
{
if (ConditionIsMet)
{
return await DoSomethingAsync();
}
else
{
return await Task.Run(DoSomethingSynchronous);
}
}
private async void MyDropDownBox_DropDownClosed(object sender, EventArgs e)
{
var updatedInformation = await UpdateSomethingAsync();
// Now use updatedInformation to update your UI controls, or the model bound to
// your UI controls.
model.Id = updatedInformation.UpdateId;
// etc...
}
}
In your event handler, you can use Invoke() to update the UI like this:
private void someEventHandler() // <- it might have params
{
// ... possibly some other code that does NOT update the UI ...
this.Invoke((MethodInvoker)delegate {
// ... it's safe to update the UI from in here ...
});
// ... possibly some other code that does NOT update the UI ...
}
I don't know who keeps doing it, but my comments below this post keep getting deleted.
This answers the TITLE of the question, which was:
How do I marshal an event from Task.Run back to the UI thread?
When you receive an event from a different thread, this is a perfectly valid way of updating the UI.
Sicne you state that "[..] DoSomethingSynchronous [is] I/O bound" you could also make it async by wrapping the IO bound operation within DoSomethingSynchronous in a Task.Run.
So if DoSomethingSynchronous is something like
public void DoSomethingSynchronous(...)
{
// some UI work
// blocking sysnchornous IO operation
var res = IoOperation();
// some more UI work
}
you could rewrite it to.
public async Task DoSomethingSynchronous(...)
{
// some UI work
// no-UI-Thread blocking IO operation
var res = await Task.Run(() => IoOperation()).ConfigureAwait(true);
// some more UI work
}
the .ConfigureAwait(true) could maybe omited but ensures that the code after the await will be scheduled in the orignal sync-context i.e. the UI-Thread.
You then obviously need to rename the method and such, but this will make the code more maintainable if you someday can use a true asycn IO in DoSomethingSynchronous
Since UpdateSomethingAsync needs to access the UI context, it shouldn't be wrapped in a Task.Run call. (You should very rarely, need to call an async method from Task.Run, usually only if the method is implemented incorrectly and you can't fix it.)
Instead DoSomethingSynchronous should be the thing you call from Task.Run. After all, the purpose of that method is to asynchronously run a synchronous method in a thread pool thread. So only use it for the synchronous method you want run in a thread pool thread, not the (supposedly) asynchronous method that needs to access the UI context.
WinUI 3 respects the below method.
DispatcherQueue.TryEnqueue(() =>
{
//Code to Update the UI
});
Figured I'd answer this myself after some more research. Most of the other answers are correct in some way, but don't necessarily explain the whole deal in one go, so I'll try to sum up here.
This first snippet from the question works event wise, but blocks if the Synchronous path in UpdateSomethingAsync is taken. Events work because "await" automatically captures the SynchronizationContext (this is key) for the UI thread, such that any events raised from UpdateSomethingAsync are marshalled back to the UI, via the SynchronizationContext. This is just the normal way of using async/await:
private async void MyDropDownBox_DropDownClosed(object sender, EventArgs e)
{
//This blocks if the DoSomethingSynchronous path is taken, causing UI to
//become unresponsive, but events propagate back to the UI correctly.
await UpdateSomethingAsync();
}
Task.Run works in much the same way, if you aren't using it to run an async method. In other words, this works without blocking and will still send events to the UI thread, because UpdateSomethingAsync is replaced with a Synchronous method. This is just the normal usage of Task.Run:
private async void MyDropDownBox_DropDownClosed(object sender, EventArgs e)
{
//UpdateSomethingAsync is replaced with a Synchronous version, and run with
// Task.Run.
await Task.Run(UpdateSomethingSynchronously());
}
However, the original code in question is Async, so the above doesn't apply. The question poses the following snippet as a possible solution, but it errors out with an Illegal Cross Thread call to the UI when an event is raised, because we are using Task.Run to call an Async method, and for some reason this does not set the SynchronizationContext:
private async void MyDropDownBox_DropDownClosed(object sender, EventArgs e)
{
//This no longer blocks, but events raised from UpdateSomethingAsync
//will cause an Illegal Cross Thread Exception to the UI, because the
//SyncrhonizationContext is not correct. Without the SynchronizationContext,
//events are not marshalled back to the UI thread.
await Task.Run(()=> UpdateSomethingAsync());
}
What does seem to work is to use Task.Factory.StartNew to assign the UI SynchronizationContext to the Task using TaskScheduler.FromCurrentSynchronizationContext, like so:
private async void MyDropDownBox_DropDownClosed(object sender, EventArgs e)
{
//This doesn't block and will return events to the UI thread sucessfully,
//because we are explicitly telling it the correct SynchronizationContext to use.
await Task.Factory.StartNew(()=> UpdateSomethingAsync(),
System.Threading.CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext);
}
What also works, and is very simple but "lies" a little to the caller, is to simply wrap DoSomethingSynchronous in Task.Run:
public async Task UpdateSomethingAsync(){
if (ConditionIsMet){
await DoSomethingAsync;
}else{
await Task.Run(DoSomethingSynchronous);
}
}
I consider this a little bit of a lie, because the method is not really fully Async in the sense that it spins off a Thread Pool thread, but may never pose an issue to a caller.
Hopefully this makes sense. If any of this is proven incorrect please let me know, but this is what my testing has uncovered.

How async task doesn't block UI

I have this code:
private async void Button_Click(object sender, RoutedEventArgs e)
{
Task<int> i = LongTaskAsync(); // first break point here
int k = await i;
print("Done, i=" + i);
}
private async Task<int> LongTaskAsync()
{
await System.Threading.Tasks.Task.Delay(5 * 1000); // second break point here
return 10;
}
When debugging, I knew that LongTaskAsync was running on UI thread. So, why it doesn't block UI and how?
Information from MSDN
The method runs synchronously until it reaches its first await expression, at which point the method is suspended until the awaited task is complete.In the meantime, control returns to the caller of the method
Function button_click is declared async. This function calls an async function. Calling an async function does not run the code immediately as calling a normal function would do. Instead the code is scheduled as a request to run a Task by one of the threads in a collection of available threads that is known as the thread pool.
After scheduling the task, the caller of the async function continues with the next statements until it reaches the statement to await for the result of the scheduled task. Quite often, the await is immediately after the call to the async function, but it does not have to.
Task<int> longRunningTask = LongTaskAsync();
// because of not awaiting, your thread is free to do other things:
DoSomethingElse();
// now I need the result: await for the longRunningTask
int i = await longRunningTask;
ProcessResult(i);
Whenever one of the threads in the thread pool is free, it will check if a piece of code is scheduled and if so it will run the code. If this thread calls another async function, the task is scheduled etc.
Most async functions will somewhere await for the results of the tasks they scheduled. In fact, your compiler will warn you if you forget to await the task. To enable your callers to await for your async function to complete, your function returns Task or Task<TResult> instead of void or TResult.
The only exception is the event handler: this method can return void. This has the effect that the task will be scheduled, but that the caller cannot await for it.
This caller is your application. The application schedules a task to handle the button clicked event but does not await until this event is finished. Hence your application will remain responsive.
Because button_click is async, your UI thread does not wait for this function to finish. Hence your program could enter this function again before LongTaskAsync is finished. Quite often this is an undesired effect and you should take care that this does not happen, for instance by disabling the button at the entrance of the event function and enable it when the event is finished. Something like this:
private async void Button_Click(object sender, RoutedEventArgs e)
{
// Prevent entering again before this function is finished:
Button clickedButton = (Button)sender;
clickedButton.Enabled = false;
// process the clicked button:
Task<int> i = LongTaskAsync(); // first break point here
int k = await i;
print("Done, i=" + i);
// Finished processing, enable the button again:
clickedButton.Enabled = true;
}
Because async/await construction is running asynchronously - so it doesn't block UI. In your method "int k = await i;" runs asynchronously.
"Use the async modifier to specify that a method, lambda expression, or anonymous method is asynchronous. The method runs synchronously until it reaches its first await expression, at which point the method is suspended until the awaited task is complete. In the meantime, control returns to the caller of the method". That's why your UI is not blocked.
I had the same question when I tried writing Android code. Background task is different from the foreground UI task and they run independently.
AsyncTasks usually uses up threads in the foreground to update UI and do UI related tasks
For example,
AsyncTask(params)-> {
#Override
protected void onPreExecute() {
super.onPreExecute();
..........
}
#Override
protected String doInBackground(String... params) {
...............
}
#Override
protected void onProgressUpdate(Integer... values) {
...............
}
#Override
protected void onPostExecute(String result) {
.................
}
}
Here, each of the tasks could be handled in its own UI thread without interfering with the background job and when the background job is finished, you could update the UI. In the mean time, you could use some UI animation to keep the UI busy.
Your sample is not enough to test the asynchronous behavoiur, I tested the behavior with a console application.
static void Main(string[] args)
{
string value1 = "First";
Console.WriteLine(value1);
GetSecondFromAsync();
string value3 = "Third";
Console.WriteLine(value3);
string value4 = "Fourth";
Console.WriteLine(value4);
Console.ReadKey();
}
private static async void GetSecondFromAsync()
{
await Task.Delay(3000);
Console.WriteLine("Second");
}
Information from MSDN,
The method runs synchronously until it reaches its first await
expression, at which point the method is suspended until the awaited
task is complete.In the meantime, control returns to the caller of the
method
At the point of first await in GetSecondFromAsync(), only this method is suspended and the control returns to the caller and executes the remaining.
So the output will be like
First
Third
Fourth
Second
That means the thread comes in the Main is not blocked, it goes on..

Crash from Invoke when MainForm is Disposed in FormClosing

I have an issue that relates to threading, cleaning up unmanaged resources and shutting down my app.
In the main UI thread I have a method that creates a new instance of class Worker. In Worker's constructor I start a new thread that has a while(Scanning) loop that updates some controls in my UI using Invoke() continuously (until Scanning bool is set to false). In the UI thread I raise the event FormClosing() whenever the application is closing down (through X button or Application.Exit() etc.). In FormClosing() I set Scanning to false and do some cleanup of unmanaged resources (that can only be done after the worker thread is done, because it uses those resources. The problem is that when I close the app down the MainForm apparently gets instantly disposed, so the app crashes at the Invoke (because it is trying to make a delegate run from UI thread, but that thread is disposed).
In an attempt to make the worker finish before the UI closes I tried to create a method StopWorker() in the worker class where I put Scanning = false, and then Thread.Join. As you can imagine the Join caused a deadlock as it makes the UI thread sleep but the Invoke needs the UI thread to move on.
In summary I need to cleanup unmanaged resources in FormClosing. I need the worker thread to be done before I do that though, as it uses these resources. The worker thread cannot finish (it uses Invoke) if the MainForm is disposed, therefore creating a tricky situation.
Based on Hans Passant's answer here, I created the below solution. It seems to be working very well.
In UI class/thread:
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
var button = sender as Button;
if (button != null && string.Equals(button.Name, #"CloseButton"))
{
//FormClosing event raised by a user-created button action
}
else
{
//FormClosing event raised by program or the X in top right corner
//Do cleanup work (stop threads and clean up unmanaged resources)
if (_bw.Scanning)
{
_bw.Scanning = false;
ClosePending = true;
e.Cancel = true;
return;
}
//Code to clean up unmanaged resources goes here (dummy code below)
ApplicationLogger.Get.Log("Doing final cleanup work and exiting application...");
MemoryHandler.Get.Dispose();
ApplicationLogger.Get.Dispose();
}
}
My worker thread is in another class that has a public bool property called Scanning. It also has this while loop (notice the line at the bottom):
private void Worker()
{
while (Scanning)
{
Thread.Sleep(50);
_sendBackValue[0] = "lbOne";
_sendBackValue[1] = "blaBla";
_synch.Invoke(_valueDelegate, _sendBackValue);
_sendBackValue[0] = "lbTwo";
_sendBackValue[1] = "blaBla";
_synch.Invoke(_valueDelegate, _sendBackValue);
_sendBackValue[0] = "lbThree";
_sendBackValue[1] = "blaBla";
_synch.Invoke(_valueDelegate, _sendBackValue);
}
MainForm.Get.Invoke((Action)(() => MainForm.Get.StopScanning()));
}
Finally, back in the UI class/thread I have this method:
public void StopScanning()
{
if (!ClosePending) return;
ApplicationLogger.Get.Log("Worker thread is closing the application...");
Close();
}
Could you not better use the BackgroundWorker class/control? It is much easier to use because it has already a lot of synchronization stuff in it.
But if you have a separate thread, in your FormClosing event, use:
yourThread.Abort();
yourThread.Join(); // or yourThread.Join(1000); where 1000 is some kind of time out value
in your thread use try-excpet-finally construct
try
{
// do your stuff
}
catch (ThreadAbortException)
{
// do someting when your thread is aborted
}
finally
{
// do the clean up. Don't let it take too long.
}
Note that the Join command will block further execution until the thread has stopped. Therefore, I would recommend a not too high value for the time out parameter, otherwise the user interface will be blocked and will irritate users.
Disclaimer: I do not advocate the use of Thread, ManualResetEvent and, above all, volatile in the .NET 4.5+ era, but since the .NET version was not specified I've done my best to address the problem while keeping things as backwards-compatible as possible.
Here's a solution which uses a polling variable and a ManualResetEvent to block the execution of the FormClosing handler until the loop has completed - without any deadlocks. In your scenario if you have a class-level reference to the Thread which runs the loop, you can use Thread.Join instead of ManualResetEvent.WaitOne in the FormClosing handler - the semantics will be the same.
using System;
using System.Threading;
using System.Windows.Forms;
namespace FormClosingExample
{
public partial class Form1 : Form
{
private volatile bool Scanning = true;
private readonly ManualResetEvent LoopFinishedMre = new ManualResetEvent(false);
private readonly SynchronizationContext UiContext;
public Form1()
{
this.InitializeComponent();
// Capture UI context.
this.UiContext = SynchronizationContext.Current;
// Spin up the worker thread.
new Thread(this.Loop).Start();
}
private void Loop()
{
int i = 0;
while (this.Scanning)
{
// Some operation on unmanaged resource.
i++;
// Asynchronous UI-bound action (progress reporting).
// We can't use Send here because it will deadlock if
// the call to WaitOne sneaks in between the Scanning
// check and sync context dispatch.
this.UiContext.Post(_ =>
{
// Note that it is possible that this will
// execute *after* Scanning is set to false
// (read: when the form has already closed),
// in which case the control *might* have
// already been disposed.
if (this.Scanning)
{
this.Text = i.ToString();
}
}, null);
// Artifical delay.
Thread.Sleep(1000);
}
// Tell the FormClosing handler that the
// loop has finished and it is safe to
// dispose of the unmanaged resource.
this.LoopFinishedMre.Set();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
// Tell the worker that it needs
// to break out of the loop.
this.Scanning = false;
// Block UI thread until Loop() has finished.
this.LoopFinishedMre.WaitOne();
// The loop has finished. It is safe to do cleanup.
MessageBox.Show("It is now safe to dispose of the unmanaged resource.");
}
}
}
Now, while this solution is (somewhat) tailored to your description of the problem (which I interpreted to the best of my ability), I had to make a large number of assumptions. If you want a better answer, you'll need to post a concise repro of the problem - not necessarily your production code, but at least a trimmed down working version which still has all the main nuts and bolts in place and exhibits the problem you've described.

Notifying about task finishing its work

I'm thinking of a simple way of reacting on task finishing its work. I came up with the following solution (paste it to WinForms application with a single button to test):
public partial class Form1 : Form
{
private Thread thread;
public void DoFinishWork() {
// [4]
// UI thread - waiting for thread to finalize its work
thread.Join();
// Checking, if it really finished its work
MessageBox.Show("Thread state: " + thread.IsAlive.ToString());
}
public void DoWork() {
// [2]
// Working hard
Thread.Sleep(1000);
}
public void FinishWork() {
// [3]
// Asynchronously notifying form in main thread, that work was done
Delegate del = new Action(DoFinishWork);
this.BeginInvoke(del);
// Finalizing work - this should be switched
// at some point to main thread
Thread.Sleep(1000);
}
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e) {
// [1]
// Schedule the task
ThreadStart start = new ThreadStart(DoWork);
// Schedule notification about finishing work
start += FinishWork;
thread = new Thread(start);
thread.Start();
}
}
This is meant to be a simple cancel scenario, so there will be only one thread, which will be running in parallel to the UI thread.
Is there a simpler (or more thread-safe) way of implementing this kind of notification for the Thread?
Please take into consideration two facts:
The only way i can terminate the thread is to Abort it (that's because I have no control over what is being done in the thread - 3rd party code)
Thus, I cannot use BackgroundWorker, because it only provides way of graceful termination.
Is there a simpler (or more thread-safe) way of implementing this kind of notification for the Thread?
Yes, use the TPL and let the framework worry about managing the thread
Task.StartNew(() => {
// do some stuff
}).ContinueWith((task) => {
// do some stuff after I have finished doing some other stuff
});
Or alternatively, since you are working with WinForms, use a BackgroundWorker and handle the RunWorkerCompleted event.
I mistook your notion of kill for cancel - there is no reliable way of actually killing a thread in .NET, even the documentation suggests that using Abort is more or less a gamble and gives absolutely no guarentees that the thread will actually be killed. Also, it will leave the thread and, as a consequence, the application in an unpredictable state so if you are willing to take that risk then that's up to you.
One alternative is to simply let the thread play out but just ignore the results, depending on the size of the task it might not be that big a deal.
Although you need Abort to kill the thread, you can still use the TPL. You could start that thread within the task, and wait for it as well as for an CancellationToken. When the task is cancelled before the thread finishes, you can call Abort on the thread.
It would look something like that:
// In your class:
ManualResetEvent threadFinished = new ManualResetEvent(false);
// In your calling function (button1_Click):
Task.Run( () => {
ThreadStart threadStart = new StreadStart(DoWork);
threadStart += () => { threadFinished.Set(); }
Thread thread = new Thread(threadStart);
threadFinished.Reset();
thread.Start();
WaitHandle waitCancel = cancellationToken.WaitHandle;
int waited = WaitHandle.WaitAny( new WaitHandle[]{ waitCancel, threadFinished } );
if (waited == 0 && cancellationToken.IsCancellationRequested)
thread.Abort();
else
thread.Join()
});

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