MultiThreading inside Loop and Wait Threads WinForms and C# - c#

I'm in trouble with this code. I'm using .Net (C#) with Winform Application.
I need to run RunProgram Method which has loop that make a call to a method named ListLoop.
In this function there is a forach that creates 1 thread for each element a list.
(Please Read the code before continue to read the description so you could understand what i'm talking about )
The problem is that if i dont make any control in the "for" (RunProgram Method) it starts (of course) 10 times The ListLoop Function.
So i would add in that "For" a code which wait that all Threads are terminated, so i can do Something and Then continue with the next loop.
I tried thread.join() but it freeze my UI application
(it's Application which inside has a WebControl Browser).
Even if i Try to play with returnThred and with thread.isAlive it still freezes UI.
If i hadn't the Multithread i'll dont stay here with those problems but it's the only good solution for my program i think.
Is there a simple solution for my code?
Update: Maybe it'snt clear my question.
I just want run the ListLoop X times but before start the next one i want wait that all threads are dead (That ones of the first call) so i can do some control and continue with the loop inside RunProgram.
Update2 I have this UI application which has a WebBrowser Control. I have a List of Links Object (each element of this class has string url and idHost =1 2 3 4...1 for google 2 for yahoo etc...)
I want make a loop where my program start a newTab (with Method AddTab(url) ) for each element of the list. When all links are opened (and so all the threads are deads and) i need to do something that count how many pages opened and who was the idHost save it and start another Loop with the list(This list take random element from a Bigger List)
Update 3 I just tried with BackGround Worker but i cant use it cause the WebKit that i'm using give COM error. Something for the Tasks.
Thanks
private void RunProgram()
{
List<Links> TheList = new List<Links>();
//Do something to Populate the List
List<System.Threading.Thread> returnThread = new List<.....>();
for(int i=0; i<10; i++)
{
returnThread=ListLoop(TheList);
// ???????????
// When Loop Method has finished and all threads stopped
// Do something
// Continue for the next Loop
}
}
private List<System.Threading.Thread> ListLoop(List<Links> list)
{
List<System.Threading.Thread> threading = new List<System.Threading.Thread>();
foreach (Links link in list)
{
Links tmp = new Links();
tmp = link;
var thread = new System.Threading.Thread(p =>
{
lock (l)
{
Action action = () =>
{
AddTab(tmp);
};
this.Invoke(action);
if (tmp.idHost == 1) //if IDhost == Google wait 5sec
{
System.Threading.Thread.Sleep(5000);
}
else
{
System.Threading.Thread.Sleep(2000);
}
}
});
threading.Add(thread);
thread.Start();
}
return threading;
}

If RunProgram is called from your main application, it will freeze your main form if it sleeps or waits for threads to terminate. You should run the RunProgram method in its own thread, so it can then create the worker threads, and then you can wait for the threads to complete in your for loop before starting new ones.
You could use AutoResetEvent to signal when threads are finished so you can simply wait on the AutoResetEvent before continuing the loop. The BackgroundWorker class may be a good class to look at for creating your threads as well.

I'm not sure if I understood your question properly, but:
You work asynchronously here... You can't wait in your code without stopping the GUI.
I think your solution will be to separate your function into 2 parts - The 1st one you just written, and the second one works after the threads are all dead.
For the second part (check the threads) I'd use either another thread (That waits until the threads are dead than continues to your code) or a Timer if you'd like to save threads and integrate easily into the main form
UPDATE:
Here is an example of how a blocking function that doesn't block the GUI thread:
using System.Windows.Forms;
using System.Threading;
using System;
namespace Threads
{
public partial class Form1 : Form
{
public event EventHandler OnSomethingFinishes;
public Form1()
{
InitializeComponent();
OnSomethingFinishes += new EventHandler(Form1_OnSomethingFinishes);
}
void Form1_OnSomethingFinishes(object sender, EventArgs e)
{
this.Invoke(new EventHandler(Form1_OnSomethingFinishesSafe), sender, e);
}
void Form1_OnSomethingFinishesSafe(object sender, EventArgs e)
{
this.Text = "Done!";
}
private void BlockingFunction(object a_oParameter)
{
// Do something that blocks
Thread.Sleep(2000);
if (OnSomethingFinishes != null)
OnSomethingFinishes(this, null);
}
private void button1_Click(object sender, EventArgs e)
{
Thread l_oThread = new Thread(BlockingFunction);
l_oThread.Start();
this.Text = "Please Wait...";
}
}
}
button1 starts the process. Notice that you have to invoke the function after the event is handled to move the control to the main GUI thread
Hope this helps

Related

C# Form Application - Stop an ongoing process with a click of another button

Is it possible to stop an ongoing process with a button click in Windows form application?
For example, let's say there are 2 buttons, "START" and "STOP"
When you press "START", it will start an infinite loop, printing numbers from 1 to infinity.
When I press "STOP", the process should stop at that moment.
But the problem is, I cannot press the "STOP" button as it does not allow me, since there's an ongoing process.
Is there a way to overcome this?
I know there's something called "MethodInvoker", but I have no idea how that works or whether it is relevant to this.
private bool keepRunning = true;
public Form1()
{
InitializeComponent();
}
private void StartBtn_Click(object sender, EventArgs e)
{
var number = 1;
while (keepRunning)
{
Thread.Sleep(1000);
MesgeLabel.Text = "" + number++;
}
}
private void StopBtn_Click(object sender, EventArgs e)
{
//Cannot even click this button
keepRunning = false;
//or
Application.Exit();
}
EDIT 1:
If you need to interact with UI controls, doing it from a background task would throw invalid operation -> illegal cross thread exception. To overcome this,
check Control.InvokeRequired
if(myLabel.InvokeRequired)
myLabel.Invoke(new Action(() => myLabel.Text = newText));
else
myLabel.Text = newText;
You can start a Task by providing a CancellationToken and cancel the operation when the stop button is clicked.
The task will execute the infinite loop on another thread and your main thread (the UI thread) should not be affected and should be accessible.
Try this:
/*
Please add these on top of your form class
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
*/
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
CancellationTokenSource cancellationTokenSource;
CancellationToken cancellationToken;
private void CountToInfinity()
{
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
Debug.WriteLine(new Random().Next());
}
}
private async void button1_Click(object sender, EventArgs e)
{
if (cancellationTokenSource == null)
{
cancellationTokenSource = new CancellationTokenSource();
cancellationToken = cancellationTokenSource.Token;
Task.Run((Action)CountToInfinity, cancellationToken);
}
}
private void button2_Click(object sender, EventArgs e)
{
if (cancellationTokenSource != null)
{
cancellationTokenSource.Cancel();
cancellationTokenSource.Dispose();
cancellationTokenSource = null;
}
}
}
If you have spawned a new process then you can call kill method.
Process myProcess = Process.Start("Notepad.exe")//starts new process
myProcess.Kill();// kills the process. save reference to myProcess and call kill on STOP button click
If you have started new thread then call abort method to stop the thread.
Thread thread = new Thread(new ThreadStart(method));
thread.Start();
thread.Abort(); // terminates the thread. call abort on STOP button click
When you press the "start" button, the code that runs and prints the numbers will run on the ui thread. (from your explanation, i assume that all you have is the message handler for the button press event and nothing else. e.g.: Not setting up a seperate thread.).
Running an infinite loop on the ui thread means, that you do not get any more time for processing other messages. (the thread that is responsible for processing the ui messages is stuck in your infinite loop.)
So, in order to be able to press the "stop" button, you need to run the code with the infinite loop in a different thread or in a different process altogether. This is what Arjun is trying to tell you. (if you want the code in the infinite loop to access resources from your form app, you need a thread. [the thread is inside the forms app process.])
please note: if you create a thread and run your number printing code inside that thread, this will not be the ui thread. Thus, you will not be able to interact with the forms controls as if you'd be on the ui thread. (i.e.: trying to set the windows.text in order to display your numbers will most likely throw an exception.)

C# winform backgroundworker

I am currently working on a home project for myself.
The program is written in C# using winforms.
The problem I'm currently experiencing is as followed:
I have a listview in my mainform called lvwGames
When I run the program without debugging, it runs fine.
However when I start with a debug, I get an error. This has something to do with the background worker thread.
Allow me to post some code to assist me.
private void MainViewLoad(object sender, EventArgs e)
{
RefreshGamesListView();
}
Nothing special here.
The reason I am calling RefreshGamesListView() is because I have to refresh on several occasions.
The method being called looks like this.
public void RefreshGamesListView()
{
pbRefreshGamesList.Value = 0;
bgwRefreshList.RunWorkerAsync();
}
So when the method is called, the background worker is called and runs the dowork method.
This one is quite big.
private void BgwRefreshListDoWork(object sender, DoWorkEventArgs e)
{
List<Game> games = _mainController.RetrieveAllGames();
int count = 1;
foreach (Game game in games)
{
string id = game.id.ToString();
var li = new ListViewItem(id, 0);
li.SubItems.Add(game.title);
li.SubItems.Add(game.Genre.name);
li.SubItems.Add(game.Publisher.name);
li.SubItems.Add(game.Platform.name);
li.SubItems.Add(game.CompletionType.name);
li.SubItems.Add(game.gameNotice);
lvwGames.Items.Add(li);
double dIndex = (double)(count);
double dTotal = (double)games.Count;
double dProgressPercentage = (dIndex / dTotal);
int iProgressPercentage = (int)(dProgressPercentage * 100);
count++;
bgwRefreshList.ReportProgress(iProgressPercentage);
}
}
When i run the code in debug, when the code is on lvwGames.Items.Add(li);
It gives me the following error:
Cross-thread operation not valid: Control 'lvwGames' accessed from a thread other than the thread it was created on.
I have absolutely no clue why.
I think it is code specific. But it can also mean I don't get the background worker completely, and specifically when to use it properly.
The reason I'm using it is because I'm loading a large large list from the database, I want to keep responsiveness in the UI when the list is loaded, and inform the users how far it is, using a progress bar.
If any code is missing, or you actually understand why this is happening PLEASE explain me why in this case its causing the error. You don't need to fix it for me. I just want to know WHY it's caused.
Thanks for taking the time to read this post. I hope to be able to continue using the debugger soon. :)
You need to call Conrol.Invoke when accessing visual controls from background threads.
if (_lvwGames.IsHandleCreated) {
Action addGameToList = () => {
string id = game.id.ToString();
var li = new ListViewItem(id, 0);
li.SubItems.Add(game.title);
....
_lvwGames.Items.Add(li);
};
if (_lvwGames.InvokeRequired) {
_lvwGames.Invoke(addGameToList);
} else {
addGameToList();
}
}
From Manipulating Controls from Threads
...For example, you might call a method that disables a button or
updates a display on a form in response to action taken by a thread.
The .NET Framework provides methods that are safe to call from any
thread for invoking methods that interact with controls owned by other
threads. The Control.Invoke method allows for the synchronous
execution of methods on controls...
This is because you're attempting to access a UI control (lvwGames) from a background thread. The way to make it work requires you to marshal the information back to the main UI thread and update the control from there:
private void BgwRefreshListDoWork(object sender, DoWorkEventArgs e)
{
List<Game> games = _mainController.RetrieveAllGames();
int count = 1;
foreach (Game game in games)
{
string id = game.id.ToString();
var li = new ListViewItem(id, 0);
li.SubItems.Add(game.title);
li.SubItems.Add(game.Genre.name);
li.SubItems.Add(game.Publisher.name);
li.SubItems.Add(game.Platform.name);
li.SubItems.Add(game.CompletionType.name);
li.SubItems.Add(game.gameNotice);
// This is the new line you need:
lvwGames.Invoke(new MethodInvoker(delegate { lvwGames.Items.Add(item) }));
double dIndex = (double)(count);
double dTotal = (double)games.Count;
double dProgressPercentage = (dIndex / dTotal);
int iProgressPercentage = (int)(dProgressPercentage * 100);
count++;
bgwRefreshList.ReportProgress(iProgressPercentage);
}
}
Normally you would check the InvokeRequired property first as mentioned in other answers, but there is really no need if you are always calling it from the background thread. Your DoWork method will always require an invoke call, so you might as well just go ahead and write it like that.
This happening cause, just like compiler cliams, you are going to update UI control content from another thread. You can not do that, as UI control can be updated only within main thread.
Please have look on this SO answer with example code provided:
Invoke from another thread
The background worker is not working properly if you run in debug mode in studio. If you have calls that use the windows handle to retrieve messages, then they will fail. If you for instance have a progressChanged event handler and this changes a text in a textbox that might fail.
I had this scenario: A Form that has a background worker. If I just start the worker without getting a dialog box up first then it works ok. If I show a dialog and then start the background worker then it fails. When I run the program normally it does not fail. It is somehow the debug environment that destroys the link between the events and the foreground window. I have changed my code to use invoke, and now all works both in when running in release and when I debug.
Here is a link explaining what can be done to make a program thread safe.
http://msdn.microsoft.com/en-us/library/ms171728(VS.80).aspx
I did not do the same as the sample to microsoft. I made delegates, assigned to the functions I needed to run. and called invoke on them.
sample pseudo code:
class MyClassWithDelegates
{
public delegate void ProgressDelegate( int progress );
public ProgressDelegate myProgress;
public void MyProgress(int progress)
{
myTextbox.Text = ..... ; // this is code that must be run in the GUI thread.
}
public MyClassWithDelegates()
{
myProgress = new ProgressDelegate(MyProgress);
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
Invoke( myProgress, e.ProgressPercentage );
}
}
All code that potentially have to be run in the GUI thread of the application must be Invoked to be safe.

Pause and Resume a Thread

I have this code to pause and resume a thread:
public partial class frmMain : Form
{
(...)
ManualResetEvent wait_handle = new ManualResetEvent(true);
(...)
}
private void frmMain_Shown(object sender, EventArgs e)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(TheLoop));
}
private void TheLoop(object stateinfo)
{
bool hasInfo = true;
while (doLoop)
{
wait_handle.WaitOne();
bool hasLines = GetInfo();
if (hasLines)
{
//Consuming time Operation 1
System.Threading.Thread.Sleep(7000);
if (CurrentLine < line.Count - 1)
CurrentLine++;
else
{
bool hasInfo2 = GetInfo2();
if (hasInfo2)
{
//Consuming time Operation 2
System.Threading.Thread.Sleep(7000);
}
CurrentLine = 0;
}
}
else
System.Threading.Thread.Sleep(40000); //Wait to query again
}
}
private void btnPauseResume_Click(object sender, EventArgs e)
{
if (btnPauseResume.Text == "Pause")
{
btnPauseResume.Text = "Resume";
wait_handle.Reset();
}
else
{
btnPauseResume.Text = "Pause";
wait_handle.Set();
}
}
The code above shows a cycle information, it works find to pause and resume the "first consuming time operation" but doesn't work for the second one, if I press the button to pause the thread in the second consuming time operation, this one continues and when the first one appears again, then it pauses there.
What am I missing here?
Thx
Have you considered using a Background Worker instead since you are using WinForms? It would probably be easier than trying to 'Pause' a thread. You can check the CancellationPending property to see if a user has elected to cancel the operation. The link has a good sample to look at.
I have never seen someone pausing a thread. Create a delegate and event inside the class or method that you are executing on a separate threat. Execute that event whenever you wish to pause your thred.
There is not any reason that I can see that would prevent a second call to WaitOne from working if placed before the 2nd time consuming operation. Since you are using a ManualResetEvent the wait handle's state will persist until either Set or Reset is called. That means if you resume the thread by calling Set then both calls to WaitOne will pass through. Likewise, if you pause the thread by calling Reset then both calls to WaitOne will block. Of course, it will not be possible to predict where the worker thread will pause if there is more than one call to WaitOne.
Got it guys! the thing is where you put the WaitOne(). For instance, if I have a While Loop (like my example) if I put the wait before it, no matter how many times I hit the pause button, it won't stop the thread, it's logic since the loop already began, but if I put it at the end, then it will work.
Appreciated your help.

[C# windows form]When invoking a listview in a thread, other controls doesnt work

I am new to C#, I hope my description of the problem is readable.
Here's my problem, I am developing a app for a win6.5 mobile. The App should have some memu items, one is 'scan', when clicked, it scans repeatedly the wifi access points nearby, and displays them on a listview. So i create a thread with a while loop for scanning every 10 seconds, i also use listview.invoke to make the listview accessible in the thread. Things looks fine when 'scan' is clicked, however, other menu items cannot be clicked due to the running of the while loop thread. I stuck here for several days, many thanks for u guys help~
private void menuItemScan_Click(object sender, EventArgs e)
{
...
Thread t = new Thread(new ThreadStart(ScanThread));
t.Start();
}
private void ScanThread()
{
listView1.Invoke(new APScanCallback(APScan));
}
public void APScan()
{
while (true)
{
listView1.Items.Clear();
foreach (AccessPoint ap in wzcInterface.NearbyAccessPoints)
{
ListViewItem item = new ListViewItem(ap.Name);
item.SubItems.Add(ap.PhysicalAddress.ToString());
item.SubItems.Add(ap.SignalStrength.Decibels.ToString());
item.SubItems.Add(ap.AuthenticationMode.ToString());
listView1.Items.Add(item);
}
listView1.Refresh();
Thread.Sleep(10000);
}
}
Control.Invoke "enqueue" the method execution to the thread handling UI (in order to serialize those routine call to the other UI routine calls).
Even if you start a thread which calls Control.Invoke, the routine APSScan is executed in thread which has called Application.Run... and what I see is that APSScan never returns, causing to freeze the UI thread.
The solution is to call Control.Invoke multiple times, looping in ScanThread routine.
Using your code:
private void ScanThread()
{
while (true) {
listView1.Invoke(new APScanCallback(APScan));
Thread.Sleep(10000);
}
}
public void APScan()
{
listView1.Items.Clear();
foreach (AccessPoint ap in wzcInterface.NearbyAccessPoints)
{
ListViewItem item = new ListViewItem(ap.Name);
item.SubItems.Add(ap.PhysicalAddress.ToString());
item.SubItems.Add(ap.SignalStrength.Decibels.ToString());
item.SubItems.Add(ap.AuthenticationMode.ToString());
listView1.Items.Add(item);
}
listView1.Refresh();
}
Your code is actually running in the main thread.
listView1.Invoke(new APScanCallback(APScan));
This code submits execution of APScan in the main application thread. Just use the timer insteaf of worker thread.

Threads and waiting

I have a thread that gathers a list of URLs from a website and updates the UI as it's doing so. That works fine. But, I need the Main thread to wait until the links have been gathered. I tried to do a join, but this locks up the UI. Here is my example. As you can see at the moment the foreach loop gets called at the same time as the thread is running. I want the foreach to run after the thread has ran.
Any ideas?
/** This thread will add links to list<string> linkList **/
Thread linkListThread = new Thread(new ThreadStart(getLinkList));
linkListThread.Start();
foreach (String link in linkList)
{
txtOutput.Text += link;
}
You can use a background worker. Or have the thread method call a method in main context when it is done, passing the list of items you gathered.
EDIT
I thought I should elaborate my second approach a little.
You could prepare a list instance before creating the thread:
List<string> links = new List<string>();
Then you pass this list to the thread, which fills it:
Thread t = new Thread(new ParameterizedThreadStart(FillList));
t.Start(links);
The thread method takes the list, fills it and calls a method that shows the details in the UI:
private static void FillList(object state)
{
List<string> links = (List<string>)state;
// Fill data
this.Invoke((MethodInvoker)delegate() { HandleNewLinks(links); }));
}
The HandleNewLinks method works as one would expect:
private void HandleNewLinks(List<string> links)
{
foreach (string link in links)
// Do something...
}
Move the code that needs to run after the thread has completed into an event handler for BackgroundWorker.RunWorkerCompleted
Update: The handler is invoked on the right (calling ) thread - so you can safely update the UI.
See the code snippet on the above msdn page.
It is not clear what you want: either the application waits (and is unresponsive), or the application does not wait and remains responsive. In the latter case, you might want to disable some controls / possible actions until the list has finished loading.
A dirty workaround is to do some sort of spin waiting (Join with a timeout of a few ms, returns the result whether it is done or not) with some Application.DoEvents() calls.
Something simple would be to have your worker threads call back to the main application on completion, you then keep a count of completed threads and in your main UI do something like:
while(runningThreads != 0)
{
Application.DoEvents();
}
and have the threads call:
void OnThreadCompleted()
{
runningThreads--;
}
Better to use BackgroundWorker for this instead of creating your own threads as this has all the callback mechanisms ready to go.
We have used the Background worker for something similar and it worked well, with two observations:
Don't append text to a textbox with += because it will slow you down considerably after a few hundred lines. Use AppendText instead.
If you add a lot of info to the interface and have sleep times (during processing), the thread might 'fall asleep'. We fixed it by deleting the text in the textbox every 200 lines (the results were written to a file, so we didn't lose anything).
One alternative is simply use Invoke on the main thread:
void YourMethod()
{
Thread linkListThread = new Thread(new ThreadStart(getLinkList));
linkListThread.Start();
}
void getLinkList()
{
List<string> linkList = new List<string>();
// Your tasks
// Done
LinkListComplete(linkList);
}
void LinkListComplete(List<string> linkList)
{
if (InvokeRequired)
{
Invoke(new Action<List<string>>(LinkListComplete),linkList);
return;
}
foreach (String link in linkList)
{
txtOutput.Text += link;
}
}

Categories