Timer in einem Thread



  • In einem Thread arbeitet man mit POINTERN ... d.h. Hol dir doch bsw. einfach mal das CWnd deiner View(dann dort auch auf WM_TIMER reagieren!)...

    UINT_PTR CCNCProgrammView::StartTimer(LPVOID pParam)
    {
        UINT_PTR nTimerVal = ((CCNCProgrammView*)(((CMainFrame*)(AfxGetMainWnd())->GetActiveView())->SetTimer(ID_CLOCK_TIMER, 1000, NULL);
    
        return nTimerVal;
    }
    

    So... Das ist aber unsinnig... du startest den Timer... und beendest damit den Thread 😉

    Also kannst de es schon fast besser so machen:

    UINT_PTR CCNCProgrammView::StartTimer(LPVOID pParam)
    {
        static DWORD dwTime = ::GetTickCount();
    
        while (true)
        {
            if ((::GetTickCount() - dwTime) > 1000)
            {
                dwTime = ::GetTickCount();
                // ... Hier machst du das was du sonnst im Timer gemacht hättest...
            }
        }
    
        return 0;
    }
    

    Das kann man dann auch noch verschönern, in dem man bsw. als Param nen Pointer auf das View übergibt. Dann fügst de der View ne boolische Membervariable hinzu, die du bevor du den Thread startest auf True setzt. (Am besten noch sowas wie ne public IsTimerRunning Funktion hinzufügen.) Dann anstelle von while (true) einfach while (dieVariable) nehmen... Wenn du den Thread dann beenden willst, gehst du einfach hin und setzt die Variable auf false.

    Referenzen
    CWnd::SetTimer
    GetTickCount



  • Hey (D)Evil!

    Erstmal danke für den tollen Tipp. Funktioniert bis jetzt gar nicht mal schlecht, jetzt hab ich nur noch das Problem, dass, sobald ich den Timer mittels eines Buttons starte, das Programm einfriert und ich es nur über den Task-Manager abwürgen kann. Liegt wahrscheinlich an der Endlosschleife.
    Hier der Code der aktuellen OnTimer:

    UINT CtestappDlg::StartTimer()
    { 
    static DWORD dwTime = ::GetTickCount();
    
        while (true)
        {
            if ((::GetTickCount() - dwTime) > 1000)
            {
                dwTime = ::GetTickCount();
                // ... Hier machst du das was du sonnst im Timer gemacht hättest...
    			if(m_seconds < 59)
    	        	  m_seconds++;
    		        else if(m_seconds >= 59)
    				{
    				  m_seconds =  0;
    				if(m_minutes < 59)
    						m_minutes++;
    					else if(m_minutes >= 59)
    						{
    							m_minutes = 0;
    						if(m_hours < 23)
    								m_hours++;
    							else if(m_hours >= 23)
    								m_hours = 0;
    						}
    					}
    
    	m_nTest.Format(_T("%02d:%02d:%02d"), m_hours, m_minutes,m_seconds);
    	UpdateData(FALSE);
    		}
        }
    
        return 0; 
    }
    

    Sorry, wenn ich lästig bin, leider bin ich aber noch ein ziemlicher Anfänger was C++ angeht. Hab erst gestern gelernt, was Threads sind, deswegen tu ich mir noch ziemlich hart mit dieser ganzen Geschichte und bin deswegen für jeden noch so kleinen Tipp total dankbar 🙂



  • Pointer...

    Du musst dir in deiner Klasse CtestappDlg noch einen Getter und Setter für m_seconds, m_minutes, m_houres anlegen...

    UINT CtestappDlg::StartTimer(LPVOID lpParam)
    { 
        CtestappDlg* pDlg   = (CtestappDlg*)lpParam;
        static DWORD dwTime = ::GetTickCount();
        CString      strTime;
    
        while (true)
        {
            if ((::GetTickCount() - dwTime) > 1000)
            {
                dwTime = ::GetTickCount();
    
                if (pDlg->GetSeconds() < 59)
                      pDlg->SetSeconds(pDlg->GetSeconds() + 1);
                else if (pDlg->GetSeconds() >= 59)
                {
                    pDlg->SetSeconds(0);
    
                    if (pDlg->GetMinutes() < 59)
                            pDlg->SetMinutes(pDlg->GetMinutes() + 1);
                    else if (pDlg->GetMinutes() >= 59)
                    {
                        pDlg->SetMinutes();
    
                        if (pDlg->GetHours() < 23)
                            pDlg->SetHours(pDlg->GetHours() + 1);
                        else if (pDlg->GetHours() >= 23)
                            pDlg->SetHours(0);
                    }
                }
    
                strTime.Format(_T("%02d:%02d:%02d"), pDlg->GetHours(), pDlg->GetMinutes(), pDlg->GetSeconds());
                pDlg->SetTime(strTime)
            }
        }
    
        return 0; 
    }
    

    Bei SetTime musst de als Parameter einfach CString& nehmen und dann nicht vergessen UpdateData aufzurufen!



  • Ok, für mein Textfeld hab ich eine Membervariable namens m_nTest angelegt. Jetzt hab ich geschrieben:

    UINT CtestappDlg::StartTimer(LPVOID lpParam)
    {
        CtestappDlg* pDlg   = (CtestappDlg*)lpParam;
        static DWORD dwTime = ::GetTickCount();
        CString strTime;
    
        while (true)
        {
            if ((::GetTickCount() - dwTime) > 1000)
            {
                dwTime = ::GetTickCount();
                 .
                 .
                 .
                }
    pDlg->m_nTest.Format(_T("%02d:%02d:%02d"), pDlg->GetHours(), pDlg->GetMinutes(), pDlg->GetSeconds());
    
    pDlg->SetTime(strTime);
    pDlg->UpdateData(FALSE);
    
            }
        }
        return 0;
    }
    

    Da bekomm ich aber eine Exception, muss ich das noch irgendwie anders schreiben?



  • Wofür rufst du SetTimer auf?

    UINT CtestappDlg::StartTimer(LPVOID lpParam)
    {
        CtestappDlg* pDlg   = (CtestappDlg*)lpParam;
        static DWORD dwTime = ::GetTickCount();
        CString strTime;
    
        while (true)
        {
            if ((::GetTickCount() - dwTime) > 1000)
            {
                dwTime = ::GetTickCount();
                 .
                 .
                // DAS IST ANSTELLE VON SETTIMER! HIER MUSS DAS GEMACHT WERDEN WAS DER TIMER MACHEN SOLL!
            }
    
            strTime.Format(_T("%02d:%02d:%02d"), pDlg->GetHours(), pDlg->GetMinutes(), pDlg->GetSeconds());
            pDlg->GetDlgItem(ID_DEINES_EDITFELDES)->SetWindowText(strTime);
        }
    
        return 0;
    }
    

    Wie Lautet die Fehlermeldung? Hast du beim starten des Threads als Parameter this als LPVOID gecastet angegeben? Hast du die Getter und Setter richtig implementiert?



  • Genau in der Zeile

    pDlg->GetDlgItem(IDC_Timer)->SetWindowText(strTime);
    

    bekomm ich die Meldung:

    "Debug Assertion Failed!" Genaueres steht nicht da.

    Ich rufe den Thread folgendermaßen auf:

    m_pThread = AfxBeginThread(StartTimer,&call, THREAD_PRIORITY_LOWEST)
    

    wobei m_pThread ein Pointer von CWinThread ist.

    Ich hoffe, dass die Getter- und Settermethoden richtig implementiert sind.
    Hier z.B. die Getter- Settermethoden für die Sekunden:

    int CtestappDlg::GetSeconds()
    {
    	return  m_seconds;
    }
    
    void CtestappDlg::SetSeconds(int seconds)
    {
    	m_seconds = seconds;
    }
    


  • Getter und Setter sind richtig. Von welchem Type ist die variable call?



  • testappDlgCall call;
    

    testappDlgCall ist eine Struktur, die außerhalb der Klasse definiert wurde:

    struct testappDlgCall {
       CtestappDlg *pDoc;
       HWND hWnd;
    };
    


  • Ne, es gibt eigentlich nur noch ein Problem und das ist folgende Codezeile:

    pDlg->GetDlgItem(ID_DEINES_EDITFELDES)->SetWindowText(strTime);
    

    Da bekomm ich eine Assertion...



  • m_pThread = AfxBeginThread(StartTimer, (LPVOID)*this, THREAD_PRIORITY_LOWEST)
    

    😉 Du kannst das HWND vom Dlg Pointer nehmen...

    Dann debugg mal...



  • Hm, vielen dank erstmal für die tolle Hilfe (D) Evil, aber mit UpdateData hab ich immernoch ziemliche Probleme. Sobald ich UpdateData in der StartTimer aufrufe, bekomm ich beim Debuggen in der Zeile

    CDataExchange dx(this, bSaveAndValidate);
    

    eine Debug-Assertion.

    Sry wenn ich langsam nerve, aber ich kriegs einfach net hin.



  • Wenn dann könntest de nur pDlg->UpdateData aufrufen... wobei ich net weiß ob die public ist, was das wiederrum nciht machbar amchen würde... aber UpdateData brauchst de eh net wenn de das mit setwindowtext usw machst...



  • Super, nach 3 Tagen tickt mein Timer endlich, vielen, vielen Dank (D)Evil, ohne dich hätt ichs net geschafft 😃
    Jetzt muss ich meinen Thread nur noch korrekt abwürgen und die Sache ist perfekt 🙂

    Mit freundlichen Grüßen

    enne



  • Ah, und weil es mir gerade einfällt, gibts eine Methode mit der man erfahren kann, ob ein Thread gerade läuft?



  • UpdateData kann man in einem Thread nicht aufrufen.

    Zu deiner Frage:
    GetExitCodeThread



  • Hi, ich hab jetzt noch ein kleines Problem. Irgendwie funktioniert das nicht ganz mit GetExitCodeThread. Ich lass meinen Thread nun in einer SDI-Anwendung laufen, welche von CFormView abgeleitet wurde. Jedoch gibts hier die Klasse CEvent nicht, was die Sache noch etwas erschwert. Ich habs mal so probiert:

    void CCNCProgrammView::OnClose()
    {
    
        m_bWantExit = true;
    	m_pThread->m_bAutoDelete = false;
    	::WaitForSingleObject(m_pThread->m_hThread, 2000);
    	::AfxEndThread(0, TRUE);	
    }
    

    Klappt aber auch nicht ganz, da mein Timer nach 2 Sekunden weiterläuft.


  • Administrator

    Darf ich fragen wieso es hier kein CEvent geben soll?

    #include <afxmt.h>
    

    Und du hast die CEvent-Klasse wieder.

    Oder habe ich was missverstanden?

    Grüssli



  • Ok, sry, du hattest recht, trotzdem hab ich immer noch das Problem, das ich nicht weiß, wie ich einen Thread mittels "Abbrechen" - Button zum Stillstand bringen kann.


Anmelden zum Antworten