WinForms Threading Problem

This is a discussion on WinForms Threading Problem within the Windows Programming forums, part of the Platform Specific Boards category; I'm having a blocking issue in my WinForm app, so I implemented a thread but it is still blocking. Here ...

  1. #1
    Registered User
    Join Date
    Feb 2008
    Posts
    20

    WinForms Threading Problem

    I'm having a blocking issue in my WinForm app, so I implemented a thread but it is still blocking. Here is the relevant code:
    Code:
    private: System::Void button1_Click(System::Object^  sender, System::EventArgs^  e) {
    				  
    				
    
    				 Thread^ newLogThread = gcnew Thread(gcnew ParameterizedThreadStart(&log));
    				 this->label2->Text = "Logging";     
    				 newLogThread->Start(this);
    				 
    			
    			
    		}
    here is the log method:
    Code:
    static void log(System::Object ^obj)
    	{
    
    	SafeThread1(obj);
    
    
    	}
    The SafeThread method is where it is blocking
    Code:
    static void SafeThread1(System::Object ^obj){
    
    			Form1 ^ob = (Form1^) obj;
    			if( ob->button1->InvokeRequired){
    				logThread ^d = gcnew logThread(SafeThread1);
    				ob->Invoke(d,gcnew array<System::Object^>{ob});				
    			}
    			else{
    				
    				while (ob->connect== EDK_OK) {
    
    					ob->state = EE_EngineGetNextEvent(ob->eEvent);
    
    					// New event needs to be handled
    					if (ob->state == EDK_OK) {
    
    						EE_Event_t eventType = EE_EmoEngineEventGetType(ob->eEvent);
    						EE_EmoEngineEventGetUserId(ob->eEvent, &userID);
    
    						// Log the EmoState if it has been updated
    						if (eventType == EE_EmoStateUpdated) {
    
    							EE_EmoEngineEventGetEmoState(ob->eEvent, ob->eState);
    							const float timestamp = ES_GetTimeFromStart(ob->eState);
    
    							ob->label2->Text=" New EmoState from user "+timestamp+userID;
    					
    							ob->logEmoStateTXT(userID, ob->eState, writeHeader);
    							//writeHeader = false;
    						}
    					}else if (ob->state != EDK_NO_EVENT) {
    						ob->label2->Text = "Internal error in Emotiv Engine!";
    						break;
    					}
    
    				Thread::Sleep(1);
    				}
    
    		
    			}
    	
    	}
    Any help getting this not to block is greatly appreciated.

  2. #2
    Sweet
    Join Date
    Aug 2002
    Location
    Tucson, Arizona
    Posts
    1,807
    Woop?

  3. #3
    Registered User
    Join Date
    Feb 2008
    Posts
    20
    I looked up BeginInvoke, from what I can gather the Invoke method called in safeThread1 should do the same thing, I can tell it is blocking in safeThreads loops, but shouldn't the form pass the thread to the new thread created when invoke is called, and allow the form to be responsive?

  4. #4
    Registered User
    Join Date
    Jan 2008
    Posts
    287
    I don't think you understand this pattern you're using. This:
    Code:
    if (control->InvokeRequired)
       control->Invoke(me);
    else
    {
       ...
    }
    is to ensure that the code executed in this method will be run on the GUI thread.

    Why is your code causing the GUI to lock up? Because that's exactly what you told it to do! Since SafeThread1 is written using that pattern, it will be executed on the GUI thread.

  5. #5
    Registered User
    Join Date
    Feb 2008
    Posts
    20
    I guess I don't understand. I was thinking it did just the opposite and passed the GUI thread control to the new Thread created in the Click function.

    I was reading all day yesterday on how to do what I described but apparently I have it wrong.

    How would I pass control to the new thread. Perhaps you could point me to a good article or example that describes what I need.

    Again thank you!

  6. #6
    Sweet
    Join Date
    Aug 2002
    Location
    Tucson, Arizona
    Posts
    1,807
    Here is an example of the pattern you are going to want to implement. This is implemented in C#.
    Code:
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    using System.Threading;
    
    namespace BeginInvokeTest
    {
        public partial class Form1 : Form
        {
            private delegate void UpdateLabelText(string text);
            public Form1()
            {
                InitializeComponent();
            }
    
            private void btnStartTextUpdate_Click(object sender, EventArgs e)
            {
                Thread loggingThread = new Thread(new ThreadStart(this.Log));
                loggingThread.Start();
            }
    
            private void SetLabelText(string text)
            {
                if (this.lblUpdateLabel.InvokeRequired)
                {
                    this.lblUpdateLabel.BeginInvoke(new UpdateLabelText(this.SetLabelText), text);
                }//if
                else
                {
                    this.lblUpdateLabel.Text = text;
                }//else
            }
            private void Log()
            {
                Thread.Sleep(5000);
    
                this.SetLabelText("WHATS UP");
            }
    
            private void btnSayHello_Click(object sender, EventArgs e)
            {
                MessageBox.Show("Hello!");
            }
        }
    }
    This is a basic form with two buttons and a label, you can click the hello button while the "Log" function is running.
    Woop?

  7. #7
    Registered User
    Join Date
    Feb 2008
    Posts
    20
    I think I implemented the pattern described but get the same blocking behaivoir.

    My delegate declaration
    Code:
    public: delegate void logThread(System::Object ^obj);
    The button click function that starts the new thread and logging
    Code:
    private: System::Void button1_Click(System::Object^  sender, System::EventArgs^  e) {
    									
    				this->label2->Text = "Logging"; 
    				
    			    Thread^ newLogThread = gcnew Thread(gcnew ParameterizedThreadStart(&log));
    				newLogThread->IsBackground=true;
    				newLogThread->Start(this);
    				 
    			
    			
    		}
    Here is the log function passed above to the new thread. Just calls safeThread1
    Code:
    static void log(System::Object ^obj)
    	{
    
    	SafeThread1(obj);
    
    
    	}
    Finally the work horse of the thread that is still blocking. I was testing if ob the form invoke required but I changed it to the textBox that is being updated with the logging results. one other control is actually updated in this thread the label
    Code:
    static void SafeThread1(System::Object ^obj){
    
    			Form1 ^ob = (Form1^) obj;
    			if( ob->textBox1->InvokeRequired){
    				//logThread ^d = gcnew logThread(SafeThread1);
    				ob->BeginInvoke(gcnew logThread(ob->SafeThread1),gcnew array<System::Object^>{ob});				
    			}
    			else{
    				
    				while (ob->connect== EDK_OK) {
    
    					ob->state = EE_EngineGetNextEvent(ob->eEvent);
    
    					// New event needs to be handled
    					if (ob->state == EDK_OK) {
    
    						EE_Event_t eventType = EE_EmoEngineEventGetType(ob->eEvent);
    						EE_EmoEngineEventGetUserId(ob->eEvent, &userID);
    
    						// Log the EmoState if it has been updated
    						if (eventType == EE_EmoStateUpdated) {
    
    							EE_EmoEngineEventGetEmoState(ob->eEvent, ob->eState);
    							const float timestamp = ES_GetTimeFromStart(ob->eState);
    
    							ob->label2->Text=" New EmoState from user "+timestamp+userID;
    					
    							ob->logEmoStateTXT(userID, ob->eState, writeHeader);
    							//writeHeader = false;
    						}
    					}else if (ob->state != EDK_NO_EVENT) {
    						ob->label2->Text = "Internal error in Emotiv Engine!";
    						break;
    					}
    
    				
    				}
    
    		
    			}
    	
    	}

  8. #8
    Sweet
    Join Date
    Aug 2002
    Location
    Tucson, Arizona
    Posts
    1,807
    Sorry for the confusion. I was trying to illustrate that you only need to set the text of the label using the Invoke pattern. So
    Code:
    ob->label2->Text=" New EmoState from user "+timestamp+userID;
    Should be the only thing you need to invoke on. Not your whole logging function.

    When you call Invoke or BeginInvoke it will call the item on the UI thread which is why you are still blocking, because you are basically defeating the purpose of your thread.
    Woop?

  9. #9
    Registered User
    Join Date
    Feb 2008
    Posts
    20
    I'm not sure how to do what your describing. Are you saying the UI thread, that is the Form thread is passing control to the newly created thread and the function that is blocking when I invoke my delegate function. So in essence I might as well not be creating the new thread in the first place.

    How would I call invoke on a specific control and in my case I have two controls that are being updated. What I would like to do is have the new thread be independent from the main form thread and control the logic in the Safethread1 function so that this logging can dynamically update the textBox1 control and the label for status updating without blocking the functionality of the main form.

  10. #10
    Sweet
    Join Date
    Aug 2002
    Location
    Tucson, Arizona
    Posts
    1,807
    Just like that was shown in my code above. Focus on this:
    Code:
            private void SetLabelText(string text)
            {
                if (this.lblUpdateLabel.InvokeRequired)
                {
                    this.lblUpdateLabel.BeginInvoke(new UpdateLabelText(this.SetLabelText), text);
                }//if
                else
                {
                    this.lblUpdateLabel.Text = text;
                }//else
            }
    Notice I am only setting the text of the update label with an invoke. You would replace your call to setting .Text with a similar type of function.
    Woop?

  11. #11
    Registered User
    Join Date
    Feb 2008
    Posts
    20
    Ok, I am not trying to be ignorant but
    Code:
    ic void SafeThread1(System::Object ^obj){
    
    			Form1 ^ob = (Form1^) obj;
    			if( ob->textBox1->InvokeRequired){
    				//logThread ^d = gcnew logThread(SafeThread1);
    				ob->BeginInvoke(gcnew logThread(ob->SafeThread1),gcnew array<System::Object^>{ob});				
    			}
    			else{
    SafeThread1 is passed to the delegate, it recurses on SafeThread because that contains the code that updates the label and textbox, I'm passing it the full form object which could be the problem but I can't just pass it the textbox or label since I update both. The new thread needs to have access to both these controls. I could place all the code after else in a seperate function and pass that function to my delegate and call invoke but that would be doing the same thing as recursing on this function, although this would allow me to change the parameter to one of the specific control types rather than the full object. I think by passing the full form, it is causing the new thread to essentially take full control of the GUI thread and not just the events associated with the specific control label and textbox. I seem to be in a dilemma.

  12. #12
    Registered User
    Join Date
    Feb 2008
    Posts
    20
    I went ahead and put the work part of SafeThread in a seperate function which I passed to the delegate along with the entire form object. The program still blocks in the updateLog function as expected (at least the code is a bit more modular .
    Still broken.
    Code:
    static void SafeThread1(System::Object ^obj){
    
    			Form1 ^ob = (Form1^) obj;
    			if( ob->textBox1->InvokeRequired){
    				//logThread ^d = gcnew logThread(SafeThread1);
    				ob->BeginInvoke(gcnew logThread(ob->updateLog),gcnew array<System::Object^>{ob});				
    			
    	
    	}
    }
    Code:
    static void updateLog(System::Object^ obj){
    	Form1 ^ob = (Form1^) obj;
    	while (ob->connect== EDK_OK) {
    
    					ob->state = EE_EngineGetNextEvent(ob->eEvent);
    
    					// New event needs to be handled
    					if (ob->state == EDK_OK) {
    
    						EE_Event_t eventType = EE_EmoEngineEventGetType(ob->eEvent);
    						EE_EmoEngineEventGetUserId(ob->eEvent, &userID);
    
    						// Log the EmoState if it has been updated
    						if (eventType == EE_EmoStateUpdated) {
    
    							EE_EmoEngineEventGetEmoState(ob->eEvent, ob->eState);
    							const float timestamp = ES_GetTimeFromStart(ob->eState);
    
    							ob->label2->Text=" New EmoState from user "+timestamp+userID;
    					
    							ob->logEmoStateTXT(userID, ob->eState, writeHeader);
    							//writeHeader = false;
    						}
    					}else if (ob->state != EDK_NO_EVENT) {
    						ob->label2->Text = "Internal error in Emotiv Engine!";
    						break;
    					}
    
    				
    				}
    
    }
    Last edited by ripspinner; 12-08-2009 at 09:10 AM.

  13. #13
    Sweet
    Join Date
    Aug 2002
    Location
    Tucson, Arizona
    Posts
    1,807
    What I am trying to say is that updateLog does not need to be invoked. That is the whole problem.

    Invoke = Put on UI Thread.

    So you are putting your function of the UI Thread(Canceling the whole point of the thread)

    Invoke should be put on methods that need to affect UI object(Forms, labels, etc). So in your code

    Code:
    //The function that actually logs.
    static void UpdateLog(Object obj)
    {
        Form1 formObject = (Form1)obj;
        while (formObject->connect == EDK_OK)
        {
            formObject->state = EE_EngineGetNextEvent(formObject->eEvent);
            if (formObject->state == EDK_OK)
            {
                EE_Event_T eventType = EE_EmoEngineEventGetType(ob->eEvent);
                EE_EmoEngineEventGetUserId(ob->eEvent, &userID);
    
                // Log the EmoState if it has been updated
                if (eventType == EE_EmoStateUpdated)
                {
                    EE_EmoEngineEventGetEmoState(ob->eEvent, ob->eState);
                    const float timestamp = ES_GetTimeFromStart(ob->eState);
    
                    //
                    //Look Here:
                    //  This will be replace with a function call in Form1 detail below
                    //
                    //ob->label2->Text = " New EmoState from user " + timestamp + userID;
                    ob->SetLabelText(" New EmoState from user " + timestamp + userID);
    
                    ob->logEmoStateTXT(userID, ob->eState, writeHeader);
                }//if
            }//if
            else if (ob->state != EDK_NO_EVENT)
            {
                //
                //Look Here:
                //  This will be replace with a function call in Form1 detail below
                //
                //ob->label2->Text = "Internal error in Emotiv Engine!";
                ob->SetLabelText("Internal error in Emotiv Engine!");
    
                //Don't need this break
                //break;
            }//else if
        }//while
    }
    //
    //Form1 Methods
    //
    
    //The delegate for setting the text
    private delegate void UpdateLabelText(string text);
    
    //The Form1 function to set the label
    public void SetLabelText(string text)
    {
        if (this->label2->InvokeRequired)
        {
            this->label2->BeginInvoke(gcnew UpdateLabelText(this->SetLabelText), text);
        }//if
        else
        {
            this->label2->Text = text;
        }//else
    }
    Forgot Form1 Methods, Sorry.
    Last edited by prog-bman; 12-08-2009 at 10:57 AM.
    Woop?

  14. #14
    Registered User
    Join Date
    Feb 2008
    Posts
    20
    ok thanks. Sheeze you really had to spell it out for me in order for me to get it, lol I feel dense.

  15. #15
    Sweet
    Join Date
    Aug 2002
    Location
    Tucson, Arizona
    Posts
    1,807
    No problem. Let me know if everything worked out ok.
    Woop?

Page 1 of 2 12 LastLast
Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Memory problem with Borland C 3.1
    By AZ1699 in forum C Programming
    Replies: 16
    Last Post: 11-16-2007, 10:22 AM
  2. Someone having same problem with Code Block?
    By ofayto in forum C++ Programming
    Replies: 1
    Last Post: 07-12-2007, 08:38 AM
  3. A question related to strcmp
    By meili100 in forum C++ Programming
    Replies: 6
    Last Post: 07-07-2007, 02:51 PM
  4. WS_POPUP, continuation of old problem
    By blurrymadness in forum Windows Programming
    Replies: 1
    Last Post: 04-20-2007, 06:54 PM
  5. Laptop Problem
    By Boomba in forum Tech Board
    Replies: 1
    Last Post: 03-07-2006, 05:24 PM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21