 |
|
 |
very well!Thanks a lot!
|
|
|
|
 |
|
 |
We can always create object of Timer class and subscribe to it's Tick event.
|
|
|
|
 |
|
 |
Your article is very interesting and provides a great start.
Instead of passing ContainingControl and a Delegate, I use a strong typed delegate and call containing methods with .Invoke()
Best regards,
|
|
|
|
 |
|
 |
I have a question though:
If the progress is not done yet and I click the "x" to close the form, I will get the execption, how to resolved this? The exception happens when the thread is still running, and I close the form so the sender is gone.
|
|
|
|
 |
|
 |
catch the exception as below:
I Love Coding...
|
|
|
|
 |
|
 |
E.g, I want to download the following 4 pictures from WWW,
http://www.163.com/xxx1.jpg
http://www.163.com/xxx2.jpg
http://www.163.com/xxx3.jpg
http://www.163.com/xxx4.jpg
I know I need use multi-thread to do this task. but I dont know how
Can anyone give me some good advice? Thanks!
Best regards,
Tom
Hello world!
|
|
|
|
 |
|
 |
This is a much better example than I have been able to find elsewhere! Other implementations seem to be overly complex and often rely on the InvokeRequired property in some way. Your method is much cleaner. Cheers!
|
|
|
|
 |
|
 |
I have a form on a thread and works like a beauty, but how do I check if that form is open in the taskbar so that i dont open a new form every time and end up with 20 open in my taskbar?
|
|
|
|
 |
|
 |
Do you mean the whole application or just another form inside of an application?
If the whole application use a mutex inside the main.
bool requestInitialOwnership = true;
bool mutexWasCreated;
Mutex m = new Mutex(requestInitialOwnership, "MyMutex", out mutexWasCreated);
if ( ! mutexWasCreated )
Application.Exit();
else
Application.Run(new MyForm());
If another form, you can traverse the window names and see if it already exists or note. This requires PInvoke
Or, setup another Mutex for the other forms with a system wide guaranteed unique name (MyApp:MyForm).
Another way is when you create the form add it to some colleciton class (Hashtable) then check the collection if it exist already.
Even another way is to implement the form in a singleton pattern where it has no public constructor. Just a static method called GetInstance().
Shawn Cicoria
shawn@cicoria.com
|
|
|
|
 |
|
 |
What I have is my MainForm.
On my MainForm I have a toolbar with lets say 6 items, each opening a new form. The way I designed it is you have to close the Current form before you can select a new one from the toolbar on the MainForm. But the one item on the toolbar must be open and accessible at all time, so what I did was to create a thread in my MainForm and use that thread to call the form I want to remain open. That works perfectly.
But lets say I minimized my thread form and by accident open it agian on the toolbar in my MainForm I just want to Maximize the one that is already open instead of creating a new one, so I want to build a check on my MainForm when that specific item is clicked to see that there isn't one already open, so that I don't sit with two of them where they create changes on the one and it doesn't get applied on the other.
Any ideas on how to do that.
|
|
|
|
 |
|
 |
I'd keep track of what forms our open in some Hashtable in the Main, then when forms are opened, Add an entry. check this table to see if already running. If running, find the object and maximize it.
Then, when the launched form closes, have it raise an event that the Main is listening to OnClose to have it removed from the Hashtable.
So, the hastable contains "key" - some name of the form, perhaps Form.Name, and the Object is just the form.
This way, when you retrieve from the HT, you just cast to Form type and set FormState property as needed.
Here you may need to be concerned about threading. Not 100% you need to test it. As the Primary thread will be setting the FormState. You may need to Invoke and put a message on the Windows Message queue to that child window.
Readup on What Application.Run actually does and impact in applications. You say you launch another "Thread" to service these other windows. Not sure why you can't just create those windows on the primary thread. Generally, I'd stick to 1 primary UI thread and any long running background tasks go on alternate threads.
|
|
|
|
 |
|
 |
Thanx, I'll try the hashtable.
I'm quite new to threads, just been playing around with it to get an idea how it works, the problem I had was because I open one form on top of eachother, that the moment I minimize one, it minimizes the entire app.
How does the primary UI thread work?
Open to any suggestions.
|
|
|
|
 |
|
 |
Got it working now, but instead of using a hashtable I created a class with GlobalVars in and added a flag to check if that specific form is open or not, but now for the next problem , how do I maximize a form from the taskbar?
|
|
|
|
 |
|
 |
I'm trying to spawn multiple threads, but for some reason, my delegate does not get called:
static int threadCount = 0;
delegate void OnThreadDoneDelegate();
private void OnThreadDone()
{
threadCount--;
}
private void button1_Click(object sender, System.EventArgs e)
{
OnThreadDoneDelegate onThreadDone = new OnThreadDoneDelegate(OnThreadDone);
// endless loop on purpose
while (true)
{
if (threadCount < MAXTHREADS)
{
WorkerClass wc = new WorkerClass(this, onThreadDone, a, b);
Thread t = new Thread(new ThreadStart(wc.Run));
t.IsBackground = true;
threadCount++;
Trace.WriteLine(String.Format("[button1_click] new thread; threadcount is now {0}", threadCount));
t.Start();
}
}
}
public class WorkerClass
{
ContainerControl m_sender = null;
Delegate m_senderDelegate = null;
public WorkerClass(ContainerControl sender, Delegate senderDelegate, String fromFile, String toFile)
{
m_sender = sender;
m_senderDelegate = senderDelegate;
}
public void Run()
{
Thread.CurrentThread.IsBackground = true;
LocalRun();
}
public void LocalRun()
{
m_sender.BeginInvoke(m_senderDelegate, null);
}
}
Any suggestions?
|
|
|
|
 |
|
 |
I just ran through your code, using .NET 1.1, and all seems fine. I've put some Trace lines in to demonstrate success.
Here's my Trace console output:
Form thrad# 10
[button1_click] new thread; threadcount is now 1
Form thrad# 10
I'm now running thread 9
[button1_click] new thread; threadcount is now 2
Form thrad# 10
The thread '' (0xf5c) has exited with code 0 (0x0).
I'm now running thread 12
[button1_click] new thread; threadcount is now 3
The thread '' (0xe1c) has exited with code 0 (0x0).
Form thrad# 10
I'm now running thread 13
[button1_click] new thread; threadcount is now 4
The thread '' (0xcfc) has exited with code 0 (0x0).
Form thrad# 10
I'm now running thread 14
[button1_click] new thread; threadcount is now 5
The thread '' (0x9a4) has exited with code 0 (0x0).
I'm now running thread 15
The thread '' (0xc78) has exited with code 0 (0x0).
Threadcound = 4
Delegate executed on thrad# 10
Threadcound = 3
Delegate executed on thrad# 10
Threadcound = 2
Delegate executed on thrad# 10
Threadcound = 1
Delegate executed on thrad# 10
Threadcound = 0
Delegate executed on thrad# 10
static int threadCount = 0;
delegate void OnThreadDoneDelegate();
private void OnThreadDone()
{
threadCount--;
Trace.WriteLine("Threadcound = " + threadCount );
Trace.WriteLine ("Delegate executed on thrad# " + Thread.CurrentThread.GetHashCode().ToString() );
}
private void button1_Click(object sender, System.EventArgs e)
{
OnThreadDoneDelegate onThreadDone = new OnThreadDoneDelegate(OnThreadDone);
while (threadCount < MAXTHREADS)
{
Trace.WriteLine ("Form thrad# " + Thread.CurrentThread.GetHashCode().ToString() );
if (threadCount < MAXTHREADS)
{
WorkerClass wc = new WorkerClass(this, onThreadDone, "a", "b");
Thread t = new Thread(new ThreadStart(wc.Run));
t.IsBackground = true;
threadCount++;
Trace.WriteLine(String.Format("[button1_click] new thread; threadcount is now {0}", threadCount));
t.Start();
}
}
}
public class WorkerClass
{
ContainerControl m_sender = null;
Delegate m_senderDelegate = null;
public WorkerClass(ContainerControl sender, Delegate senderDelegate, String fromFile, String toFile)
{
m_sender = sender;
m_senderDelegate = senderDelegate;
}
public void Run()
{
Thread.CurrentThread.IsBackground = true;
Trace.WriteLine("I'm now running thread " + Thread.CurrentThread.GetHashCode().ToString() );
LocalRun();
}
public void LocalRun()
{
IAsyncResult iaRes = null;
if ( m_sender.InvokeRequired )
{
iaRes = m_sender.BeginInvoke(m_senderDelegate);
}
else
Trace.WriteLine ("invoke not required");
}
}
Shawn Cicoria
shawn@cicoria.com
|
|
|
|
 |
|
 |
Hmmm, i'm running Whidbey with beta 1.2 (v1.2.30703), that might pose a problem; but i wouldn't expect it to... Did my code spawn numerous threads but a max of MAXTHREADS at you side? My code goes into an endless loop (while(true)) without spawning any more threads, as the delegate completion is not run... Also, my form gets non-responsive with this code...
|
|
|
|
 |
|
 |
Well, I changed your while() loop to just check the MAXTHREADS value, which I set to 5 as a static "int" in the class.
If I left it, then, yes, it did go into an endless loop. But, all the background threads call the begin_invoke properly.
The loop is causing the windows thread to never pull any more messages off the message queue, so the target delegate never gets called - or I didn't wait around for it.
If you want to read the window messages for the form add a "Application.DoEvents();" call.
But I have to question the pattern here. The loop alone will consume nearly 100% of the CPU of a processor, so, the form thread will consume CPU, and those back-ground threads will not get a fair share of context switches for a single processor.
An easy way is just to put a Thread.Sleep in the loop, but there are better ways which is well beyond the scope of this article.
Shawn Cicoria
shawn@cicoria.com
|
|
|
|
 |
|
 |
To add to my last response to this, the Form thread will be in a loop, then the GUI will be unresponsive. The idea is to have the form responsive.
So, that loop itself should be on a different thread than the form's thread.
This looks like an attempt at a threadpool implementation. Look around as there are quite a few examples out there that don't rely on .NET's wrapper around the Win32 ThreadPool which has a few limitations.
But then why not just use the .NET one.
Again, this is getting way to deep for the scope of this article.
Shawn Cicoria
shawn@cicoria.com
|
|
|
|
 |
|
 |
Ofcourse the while(true) loop is not meant to be like that, it's just a simplification... Thanks for your insights, i will look around. Might be a good idea to have some article about .NET multithreading with callbacks...
|
|
|
|
 |
|
 |
But that simplification was the root of the problem
Also, with the changes, does it work on Longhorn/Whidbey?
Shawn
|
|
|
|
 |
|
 |
Wow, your explanation made it clear to me
Now I know what delegates can do and how to use them. I read a alot about delegates but I never got a real world sample.
One question though: In one project I used a different approach to update the GUI from a thread. I built the WorkerThreadClass and defined public events. For example: I looped through a collection of values and fired an event everytime. In my forms class I updated my progressbar in the eventhandler. Are there some drawbacks or dangerous things in this approach?
-thx
|
|
|
|
 |
|
 |
Not seeing the code, the only way to be 100% sure is to run a method check. I'd like to see the code to be 100% sure.
A check is to add a method call to the control your trying to update (try it in debug mode) to see if "invoke required".
For example, if your control's instance name is 'myControl', the myControl.InvokeRequired returns true if you're running in a different thread than the creator. False, if on the creator thread.
Check Chris Sell's article (the first I referenced) for another example.
What's the danger? Regular synchronization issues that occur with multi-threaded access to memory - overwrites, misreads, etc.
Shawn Cicoria
shawn@cicoria.com
|
|
|
|
 |
|
 |
I'm not sure if you're on the right track. Showing the code would be going too far here, but I can explain the implementation structure in more detail:
I have a FormClass with a ProgressBar on it. I have a WorkerThreadClass which will be started synchronously. The WorkerThreadClass processes a loop. Everytime a loop is processed an event (public) will be raised. The event is handled in the FormClass which started the WorkerThread, the EventArgs are ElementCount and MaxElements. The eventhandler in the FormClass will actually update the progressbar with the EventArgs values.
So there is never any direct access to the ProgressBar from the WorkerThreadClass.
Any thoughts and comments?
|
|
|
|
 |
|
 |
OK, just got the time to look at this. I just wanted to be sure, but the thread the event is fired upon, runs through the registered method instances (registered using +=) on it's thread, not the thread of the Form.
So, as I expected, the statement in the new method ShowProgress2 below "bool rc = textBox1.InvokeRequired;" returns True. I also traced out the hash of the CurrentThread in each, and the thread of execution through the registered method for the event is different than the form's thread.
Now, it worked in my testing to update the control's property, (Text), but it does violate the rule of not updating a control on a different thread than the creator thread. So, again, the results may be different for other calls, but eventually many fail.
Add to WorkerClass the following:
=======================================
public delegate void OnUpdate(int messagesDone);
public event OnUpdate onUpdate;
private void FireEvent(int numMessages)
{
onUpdate( numMessages );
}=======================================
The, I modify the LocalRunProcess to fire the event with each loop:
=======================================
FireEvent( i );
=======================================
I add to the form:
=======================================
WorkerClass wc = new WorkerClass( this, showProgress, new object[] { imsgs } );
Trace.WriteLine( "Form Thread: " + Thread.CurrentThread.GetHashCode());
wc.onUpdate += new WorkerClass.OnUpdate( ShowProgress2 );
=======================================
Then add another method to match the EventHandler delegate added to WorkerClass
=======================================
public void ShowProgress2 ( int numMessages)
{
bool rc = textBox1.InvokeRequired;
Trace.WriteLine( "Event Thread: " + Thread.CurrentThread.GetHashCode());
textBox1.Text = numMessages.ToString();
}
=======================================
Shawn Cicoria
shawn@cicoria.com
|
|
|
|
 |
|
 |
Thank you very much for your input on this !
I wrote a little test-program myself. You are absolutely right, but this means, that the event which is handled in the form's class code isn't really executed in the form's thread but in the worker thread. Is this correct?
A suggestion to your article might be to post a sample which fires events from the worker thread to update the GUI.
Again, thanks a lot to clear this but now I have to fix a project of mine
|
|
|
|
 |