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;
}
}
Related
I have a logical problem i am not sure how to solve.. Basically i have a program that starts threads based on a numericUpDown value, if the user selects 5 in the numericUpDown box 5 threads will start.
The problem is that the user also has a listbox they can fill in with info, which will be used in the threads..
So what i want to be able to do in my loop instead of looping it 5 times from the numericUpDown value is if; lets say the user enteres 10 items in the listBox, and selects to use 5 threads.. i then want all the listBox items to be queued but only have 5 run at a time..
How would i accomplish this?
Oh if it matters this is how i start my threads:
Thread thread = new Thread(() => doTask(numeret));
thread.IsBackground = true;
thread.Start();
I believe you wish to use a ThreadPool, as explained here:
http://msdn.microsoft.com/en-us/library/system.threading.threadpool.aspx
You need to specify the number of threads to use, and then use ThreadPool.QueueUserWorkItem to queue your tasks.
Alternatively, you can use the parallel extensions to LinQ to perform asynchronous tasks (not the same as multithreading) - and specify the .MaxDegreesOfParalallism() value (which only sets the upper maximum)
itemsToProcess.AsParallel().MaxDegreesOfParalallism(numThreads).ForAll(item =>
{
doTask(item);
});
Usually, something like this is done using worker threads. You create a list of work items (= your listbox entries):
List<WorkItem> myWorkItems = ...; // contains 10 items
And you create your threads. You do not, however, assign a work item to the thread yet (as you do in your example):
for (int i = 0; i < numberOfThreads; i++) { // creates 5 threads
var t = new Thread(doWork);
t.IsBackground = true;
t.Start();
}
Each thread runs a loop, checking for new work items (thread-safe!) and doing work, until no more work is to be done:
void doWork() {
while (true) {
WorkItem item;
lock(someSharedLockObject) {
if (myWorkItems.Count == 0)
break; // no more work to be done
item = myWorkItems[0];
myWorkItems.Remove(item);
}
doTask(item);
}
}
This is similar to what the ThreadPool of the .net Framework does. The ThreadPool, however, is designed to work best when the number of threads can be chosen be the Framework. The example above gives you full control over the number of threads (which seems to be what you want).
Store the info from the listbox in a stack (for example).
Then, in the doTask() method : pop an element from the stack, do the stuff and do it again until the stack is empty.
Basically :
//Stack containing the info to process
Stack<string> infos = new Stack<string>();
//Method for the thread
void doTask()
{
while(true)
{
string info;
//Threadsafe access to the stack
lock (infos.SyncRoot)
{
//Exit the thread if no longer info to process
if (infos.Count == 0) return;
info = infos.Pop();
}
//Do the actual stuff on the info
__DoStuff(info);
}
}
I have inherited some code that queries a DB over a WCF service and then employs a callback when it's done. I am trying to add some code to that callback to update the UI as the data is processed. I'm finding that I cannot get the UI to update during that callback:
client.GetDataAsync();
client.GetDataCompleted += new EventHandler<GetDataCompletedEventArgs>(GetDataCompleted);
void GetDataCompleted(object sender, GetDataCompletedEventArgs e)
{
// Loop through the data
// ...
textBlock1.Text= "test1";
Dispatcher.BeginInvoke(() => textBlock1.Text= "test2" );
var thread = new Thread(() =>
{
// textBlock1.Text= "test3"; (this throws a cross-thread access exception)
Dispatcher.BeginInvoke(() =>
{
textBlock1.Text= "test4";
});
}
thread.Start();
// ...
Debug.WriteLine("done");
}
None of these things update the UI until (apparently) the entire callback is completed. This post:
What thread calls the completed event handler on silverlight WCF calls?
suggests that the callback is running on the main UI thread so that the BeginInvoke call should be unnecessary. Even if I add various delays in the above code, it still doesn't work. Is this possible? Is there a better way to do this?
(This is a follow-up question to this: Multiple asynchronous UI updates in Silverlight)
degorolls is right in suggesting the TPL, your code would look like below (except without the comments)(Also, exceptions MUST be handled in the TPL, so that might make it not worth it, but I dont think it should).
The first methods would remain the same, and yes in event-based async programming thread-safety is taken care of (ie: you always return to the same thread you called out from)
I also noticed that the text output is all doing = instead of +=, but that is probably more of a problem of typing into overflow
So, test1 and test2 will print out at the same time, however everything being spit out from the TPL code should print as it comes in.
UI code should not be doing anything that requires too much time, though...only updating the UI. So, do think of this as a point to refactor?
Let me know if this helps or if I missed what you were looking for.
client.GetDataAsync();
client.GetDataCompleted += new EventHandler<GetDataCompletedEventArgs>(GetDataCompleted);
void GetDataCompleted(object sender, GetDataCompletedEventArgs e)
{
// Loop through the data
// ...
textBlock1.Text= "test1";
//////Dispatcher should not be needed here as this IS on the main UI thread
Dispatcher.BeginInvoke(() => textBlock1.Text= "test2" );
//////Everything that happens here should NOT be on the main UI thread, thus the cross-thread access exception
//////You can do Dispatcher.CheckAccess to determine if you need to invoke or not
//////Notice the newCopyOfDataToBeWritten. This is a closure,
//////so using the same referenced object will result in errant data as it loops
//////Also, doing it this way does not guarantee any order that this will be written out
//////This will utilize the parallel fully, but there are ways to force the order
var task = Task.Factory.StartNew(()=>
{
Dispatcher.BeginInvoke(()=>textBlock1.Text += newCopyOfDataToBeWritten)
}
);
// ...
///////I assume this is the end of the loop?
Debug.WriteLine("done");
}
....
the below dummied-down code based on what you posted seems to work for me
var outsideThread = new Thread(()=>
{
for(int i = 0; i < 20; i++)
{
//This code will show all at once since it is on the main thread,
//which is still running
//If you want this to display one at a time also, then you need
//to use threads and callbacks like below, also
Dispatcher.BeginInvoke(()=>{textBlock1.Text += "outer" + i;});
int newI = i;
var thread = new Thread(() =>
{
System.Threading.Thread.Sleep(1000 * newI);
Dispatcher.BeginInvoke(() =>
{
//This will display as it comes in
textBlock1.Text += "inner" + newI;
});
});
thread.Start();
}
});
outsideThread.Start();
I have a label and canvas representing a coordinate system. In the canvas there is a node, which should change it's place every time a calculation is made. This is done until a specific stopping criteria is meet.
I also have a label containing coordinate info for the note. I want to be able to see the node and label update on every iteration(every time the calculation is made).
I have looked a the dispatcher, but my GUI will only update when the calculations are done. Below is what I have tried doing with the Dispatcher for the label.
While(notDone = true){
//Calculations made here
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Background,
new Action(() => this.aLabel.Text = aString ));
}
I have been looking at solutions for similar problems and some solutions involve threads. Do I have to use threads to update my GUI?
.Net has a class exactly for you, my friend!
BackgroundWorker
Do all the heavy lifting (your loop) in the DoWork method and update your GUI in ReportProgress.
See the documentation for help.
Just create delegate and event for handling information changes
public delegate void SomeInfoChangeDelegate(object o);
public event SomeInfoChangeDelegate InfoChangeEvent = null;
Assign listener to event and start thread w/ calculations
InfoChangeEvent += new SomeInfoChangeDelegate(OnInfoChanged);
Thread t = new Thread(new ParameterizedThreadStart(SomeWorkerThread));
t.Start();
Make calculation thread and event listener
void OnInfoChanged(object o)
{
// Update label
}
protected void SomeWorkerThread(object o)
{
int i = 0;
while(true)
{
if( i % 10 == 0 )
{
if(InfoChangeEvent != null)
{
InfoChangeEvent(i);
}
}
Thread.Sleep(1000);
i++;
}
}
And don't forget to stop thread :)
If you're performing calculations in UI (main) thread, it can't process messages until they're done. Move calculations to another thread and notify main thread in each iteration.
If you use WPF/Silverlight make class with INotifyPropertyChanged implemented and bind element positions to properties. On RaisePropertyChanged("PropName") position 'll be changed.
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
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.