Page 1 of 1

Problema in utilizare wxThread.

Posted: 22 Mar 2012, 13:39
by nedo
Salut, incerc sa fac o aplicatie simpla pentru a intelege mai bine cum se folosesc thread-urile si evenimentele.
Am totusi o problema. Aplicatia imi da mereu crash, cu exit code 3, dupa ce termina executia membrului void test2Frame::OnbCopiazaClick(wxCommandEvent& event). Daca merg cu step in eroarea se pare ca apare cand executia ajunge in user32.dll
Acestia sunt ulti 5 pasi pe care ii face aplicatia inainte sa dea eroare:

Code: Select all

At C:\wxWidgets-2.8.12\build\msw/../../src/msw/window.cpp:2622
At C:\wxWidgets-2.8.12\build\msw/../../src/msw/window.cpp:2623
In USER32!GetDC () (C:\WINDOWS\system32\user32.dll)
In ntdll!RtlDecompressBuffer () (C:\WINDOWS\system32\ntdll.dll)
In USER32!GetDC () (C:\WINDOWS\system32\user32.dll)
Dupa care windows-ul imi returneaza un run time error , iar in code blocks primesc exit code ox03. Acesta presupune ca nu gaseste un anumit path, totusi nu imi dau seama care anume este acel path.
Sunt destul de sigur ca fac ceva gresit in utilizarea wxThread si a eventului, totusi nu reusesc sa imi dau seama.
wxWidgets a fost compilat cu mingw utilizat wxMSW, cu librari shared, nonmonolitic.

Proiectul este format din 4 fisiere. Le aveti mai jos.

test2App.h

Code: Select all

/***************************************************************
 * Name:      test2App.h
 * Purpose:   Defines Application Class
 * Author:    me (me)
 * Created:   2012-03-20
 * Copyright: me ()
 * License:
 **************************************************************/

#ifndef TEST2APP_H
#define TEST2APP_H

#include <wx/app.h>

class test2App : public wxApp
{
    public:
        virtual bool OnInit();
};

#endif // TEST2APP_H
test2App.cpp

Code: Select all

/***************************************************************
 * Name:      test2App.cpp
 * Purpose:   Code for Application Class
 * Author:    me (me)
 * Created:   2012-03-20
 * Copyright: me ()
 * License:
 **************************************************************/

#include "test2App.h"

//(*AppHeaders
#include "test2Main.h"
#include <wx/image.h>
//*)

IMPLEMENT_APP(test2App);

bool test2App::OnInit()
{
    //(*AppInitialize
    bool wxsOK = true;
    wxInitAllImageHandlers();
    if ( wxsOK )
    {
    	test2Frame* Frame = new test2Frame(0);
    	Frame->Show();
    	SetTopWindow(Frame);
    }
    //*)
    return wxsOK;

}
test2main.h

Code: Select all

/***************************************************************
 * Name:      test2Main.h
 * Purpose:   Defines Application Frame
 * Author:    me (me)
 * Created:   2012-03-20
 * Copyright: me ()
 * License:
 **************************************************************/

#ifndef TEST2MAIN_H
#define TEST2MAIN_H

//(*Headers(test2Frame)
#include <wx/menu.h>
#include <wx/panel.h>
#include <wx/button.h>
#include <wx/frame.h>
#include <wx/statusbr.h>
//*)
#include <wx/thread.h>
#include <iostream>
#include <string>
#include <fstream>
#include <wx/filedlg.h>

// clasa frame - fereastra aplicatiei, cu toate butoanele si membrii necesari
class test2Frame: public wxFrame
{
    public:

        test2Frame(wxWindow* parent,wxWindowID id = -1);
        virtual ~test2Frame();

    private:

        //(*Handlers(test2Frame)
        void OnQuit(wxCommandEvent& event);
        void OnAbout(wxCommandEvent& event);
        void OnbSelFisIntClick(wxCommandEvent& event);
        void OnbSelFisIesClick(wxCommandEvent& event);
        void OnbCopiazaClick(wxCommandEvent& event);
        //*)
        void onThreadFinished(wxEvent& event);
        //(*Identifiers(test2Frame)
        static const long ID_BSelFisInt;
        static const long ID_bSelFisIes;
        static const long ID_BCopiaza;
        static const long ID_PANEL1;
        static const long idMenuQuit;
        static const long idMenuAbout;
        static const long ID_STATUSBAR1;
        //*)

        //(*Declarations(test2Frame)
        wxPanel* Panel1;
        wxButton* bCopiaza;
        wxButton* bSelFisInt;
        wxStatusBar* StatusBar1;
        wxButton* bSelFisIes;
        //*)
        std::string m_PathFisierIntrare;
        std::string m_PathFisierIesire;
        wxCriticalSection m_CritSec;
        DECLARE_EVENT_TABLE()
};

// definitia clasei derivata din wxThread - aceasta e clasa care va face, in cazul meu, copiera din un fisier in altul
class workerThread: public wxThread
{
    std::string m_PathFisIntrare;
    std::string m_PathFIsIesire;
    test2Frame* m_frame;
    virtual void* Entry();
    virtual void* onExit();
    public:
        workerThread(std::string pathFisierIntrare, std::string pathFisierIesire, test2Frame* frame);
};

#endif // TEST2MAIN_H
test2main.cpp

Code: Select all

/***************************************************************
 * Name:      test2Main.cpp
 * Purpose:   Code for Application Frame
 * Author:    me (me)
 * Created:   2012-03-20
 * Copyright: me ()
 * License:
 **************************************************************/

#include "test2Main.h"
#include <wx/msgdlg.h>

//(*InternalHeaders(test2Frame)
#include <wx/intl.h>
#include <wx/string.h>
//*)

//helper functions
enum wxbuildinfoformat {
    short_f, long_f };

wxString wxbuildinfo(wxbuildinfoformat format)
{
    wxString wxbuild(wxVERSION_STRING);

    if (format == long_f )
    {
#if defined(__WXMSW__)
        wxbuild << _T("-Windows");
#elif defined(__UNIX__)
        wxbuild << _T("-Linux");
#endif

#if wxUSE_UNICODE
        wxbuild << _T("-Unicode build");
#else
        wxbuild << _T("-ANSI build");
#endif // wxUSE_UNICODE
    }

    return wxbuild;
}

using std::endl;
using std::getline;
using std::string;
using std::ifstream;
using std::ofstream;

//(*IdInit(test2Frame)
const long test2Frame::ID_BSelFisInt = wxNewId();
const long test2Frame::ID_bSelFisIes = wxNewId();
const long test2Frame::ID_BCopiaza = wxNewId();
const long test2Frame::ID_PANEL1 = wxNewId();
const long test2Frame::idMenuQuit = wxNewId();
const long test2Frame::idMenuAbout = wxNewId();
const long test2Frame::ID_STATUSBAR1 = wxNewId();
//*)

BEGIN_DECLARE_EVENT_TYPES()
    DECLARE_LOCAL_EVENT_TYPE(wxEVT_THREAD_FINISHED, 7777)
END_DECLARE_EVENT_TYPES()

DEFINE_EVENT_TYPE(wxEVT_THREAD_FINISHED)

BEGIN_EVENT_TABLE(test2Frame,wxFrame)
    //(*EventTable(test2Frame)
    //*)
    EVT_CUSTOM(wxEVT_THREAD_FINISHED, wxID_ANY, test2Frame::onThreadFinished) // eventul costum - este chemat atunci cand threadul a terminat de copiat
END_EVENT_TABLE()


test2Frame::test2Frame(wxWindow* parent,wxWindowID id)
{
    //(*Initialize(test2Frame)
    wxMenuItem* MenuItem2;
    wxMenuItem* MenuItem1;
    wxMenu* Menu1;
    wxMenuBar* MenuBar1;
    wxMenu* Menu2;

    Create(parent, id, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, _T("id"));
    Panel1 = new wxPanel(this, ID_PANEL1, wxPoint(152,328), wxDefaultSize, wxTAB_TRAVERSAL, _T("ID_PANEL1"));
    bSelFisInt = new wxButton(Panel1, ID_BSelFisInt, _("Selecteaza Fisier Intrare"), wxPoint(72,72), wxSize(264,23), 0, wxDefaultValidator, _T("ID_BSelFisInt"));
    bSelFisIes = new wxButton(Panel1, ID_bSelFisIes, _("Selecteaza fisier iesire"), wxPoint(72,136), wxSize(264,23), 0, wxDefaultValidator, _T("ID_bSelFisIes"));
    bCopiaza = new wxButton(Panel1, ID_BCopiaza, _("Copiaza"), wxPoint(72,272), wxSize(264,23), 0, wxDefaultValidator, _T("ID_BCopiaza"));
    bCopiaza->Disable();
    MenuBar1 = new wxMenuBar();
    Menu1 = new wxMenu();
    MenuItem1 = new wxMenuItem(Menu1, idMenuQuit, _("Quit\tAlt-F4"), _("Quit the application"), wxITEM_NORMAL);
    Menu1->Append(MenuItem1);
    MenuBar1->Append(Menu1, _("&File"));
    Menu2 = new wxMenu();
    MenuItem2 = new wxMenuItem(Menu2, idMenuAbout, _("About\tF1"), _("Show info about this application"), wxITEM_NORMAL);
    Menu2->Append(MenuItem2);
    MenuBar1->Append(Menu2, _("Help"));
    SetMenuBar(MenuBar1);
    StatusBar1 = new wxStatusBar(this, ID_STATUSBAR1, 0, _T("ID_STATUSBAR1"));
    int __wxStatusBarWidths_1[1] = { -1 };
    int __wxStatusBarStyles_1[1] = { wxSB_NORMAL };
    StatusBar1->SetFieldsCount(1,__wxStatusBarWidths_1);
    StatusBar1->SetStatusStyles(1,__wxStatusBarStyles_1);
    SetStatusBar(StatusBar1);

    Connect(ID_BSelFisInt,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&test2Frame::OnbSelFisIntClick);
    Connect(ID_bSelFisIes,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&test2Frame::OnbSelFisIesClick);
    Connect(ID_BCopiaza,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&test2Frame::OnbCopiazaClick);
    Connect(idMenuQuit,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&test2Frame::OnQuit);
    Connect(idMenuAbout,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&test2Frame::OnAbout);
    //*)
}

test2Frame::~test2Frame()
{
    //(*Destroy(test2Frame)
    //*)
}

void test2Frame::OnQuit(wxCommandEvent& event)
{
    Close();
}

void test2Frame::OnAbout(wxCommandEvent& event)
{
    wxString msg = wxbuildinfo(long_f);
    wxMessageBox(msg, _("Welcome to..."));
}
// constructorul clasei workerThread
workerThread::workerThread(string pathFisierIntrare, string pathFisieriesire, test2Frame* frame) : wxThread()
{
    m_PathFisIntrare = pathFisierIntrare; // adresa fisierului de intrare
    m_PathFIsIesire = pathFisieriesire; // adresa fisierului de iesire
    m_frame = frame; // pointer catre test2frame - catre fereastra din care a fost apelat threadul
}

// functia unde munca threadului este executata
void* workerThread::Entry()
{
    if(TestDestroy())
    {
        return NULL;
    }
    ifstream fIn;
    ofstream fOut;
    fIn.open(m_PathFisIntrare.c_str());
    fOut.open(m_PathFIsIesire.c_str());
    if(fIn.is_open() && fOut.is_open())
    {
        while(fIn.good())
        {
            string temp;
            getline(fIn, temp);
            fOut << temp << endl;
        }

    }
    fIn.close();
    fOut.close();
    //m_frame = NULL;
    wxCommandEvent evt(GetId(), wxEVT_THREAD_FINISHED);
    //wxPostEvent(m_frame, evt);
    m_frame->AddPendingEvent(evt);
    return NULL;
}

// functie apelata la apasarea butonului "Selecteaza fisier intrare"
void test2Frame::OnbSelFisIntClick(wxCommandEvent& event)
{
    wxFileDialog selFisInt(this, _T("Selecteaza fisier intrare."), "", "", "TXT Files(*.txt)|*.txt", wxFD_FILE_MUST_EXIST);
    if(selFisInt.ShowModal() == wxID_OK)
    {
        m_PathFisierIntrare = string(selFisInt.GetPath().mb_str());
    }
    if(!m_PathFisierIesire.empty() &&!m_PathFisierIntrare.empty())
    {
        bCopiaza->Enable(true);
    }
    wxMessageBox(_T(m_PathFisierIntrare.c_str()));
}

// functie apelata la apasarea butonului "Selecteaza fisier iesire"
void test2Frame::OnbSelFisIesClick(wxCommandEvent& event)
{
    wxFileDialog selFisIes(this, _T("Selecteaza fisier iesire."), "", "", "TXT Files (*.txt)|*.txt", wxFD_DEFAULT_STYLE);
    if(selFisIes.ShowModal() == wxID_OK)
    {
        m_PathFisierIesire = string(selFisIes.GetPath().mb_str());
    }
    if(!m_PathFisierIesire.empty() &&!m_PathFisierIntrare.empty())
    {
        bCopiaza->Enable(true);
    }
    wxMessageBox(_T(m_PathFisierIesire.c_str()));
}

// functie apelata la terminarea executiei threadului
void test2Frame::onThreadFinished(wxEvent& event)
{
    bSelFisIes->Enable(true);
    bSelFisInt->Enable(true);
    wxMessageBox("Am terminat.");
}

// functie apelata la apasarea butonului Copiaza
// functia creeaza thread-ul si il ruleaza
// dezactiveaza cele 3 butoane pentru siguranta
void test2Frame::OnbCopiazaClick(wxCommandEvent& event)
{
    bCopiaza->Enable(false);
    bSelFisIes->Enable(false);
    bSelFisInt->Enable(false);
    //m_CritSec.Enter();
    workerThread thread(m_PathFisierIntrare, m_PathFisierIesire, this);
    thread.Create();
    thread.Run();
    //m_CritSec.Leave();

}
// functie goala, apelata la terminarea executiei threadului
void* workerThread::onExit()
{

}
Daca imi poate explica si mie cineva ce anume gresesc, asa mai pe intelesul unui incepator.

Eventual daca aveti niste resurse care sa explice mai pe intelesul unui incepator cu ce se mananca threadurile, si in special wxThread. Am citit atat pe wiki-ul wxWidgets, cat si in carte dar ... nu m-au luminat .

Multumesc pentru timpul acordat.

Re: Problema in utilizare wxThread.

Posted: 22 Mar 2012, 14:28
by nedo
Heh, am rezolvat, ca de obicei chestii minore =)).
Eu defineam thread-ul pe stack, in timp ce clasa din care il creeam era definita pe heap. Am definit threadul pe heap si s-a rezolvat problema.
De asemenea pentru a putea utiliza eventul in loc de "EVT_CUSTOM(wxEVT_THREAD_FINISHED, wxID_ANY, test2Frame::onThreadFinished)" trebuia sa folosesc
"EVT_COMMAND(wxID_ANY, wx_EVT_THREAD_FINISHED, test2frame::onThreadFinished). - inversasem si 2 argumente =)).
Totusi daca mai aveti indicatii le astept cu placere.

Re: Problema in utilizare wxThread.

Posted: 22 Mar 2012, 14:44
by bu7ch3r
Dap, workerthread thread ala isi pierde scopul si se distruge cand functia in care a fost instantiat se sfarseste. Acum tre sa ai grija de pointer sa-l stergi in destructorul clasei de baza.

Cum aloci memorie pentru thread?

Nu sincroniza threadul principal cu celelalte threaduri. Main thread-ul trebuie sa mearga mereu, altfel la un moment dat o sa moara de foame toate threadurile secundare.

Re: Problema in utilizare wxThread.

Posted: 22 Mar 2012, 17:32
by nedo
Ca de obicei chestiile mici imi dau batai de cap.
Acum am realizat de ce s-ar putea sa imi dea eroare. Totusi in testul pe care l-am facut eu inlocuind partea urmatoare

Code: Select all

workerThread thread(m_PathFisierIntrare, m_PathFisierIesire, this);
thread.Create();
thread.Run();
cu

Code: Select all

workerThread thread = new worketThread(m_PathFisierIntrare, m_PathFisierIesire, this);
thread.Create();
thread.Run();
si

Code: Select all

EVT_CUSTOM(wxEVT_THREAD_FINISHED, wxID_ANY, test2Frame::onThreadFinished)
cu

Code: Select all

EVT_COMMAND(wxID_ANY, wxEVT_THREAD_FINISHED, test2Frame::onThreadFinished)
Si programul a functionat.
Acum insa realizez ca pointerul catre thread se va sterge atunci cand termina de executat functia test2Frame::OnbCopiazaClick(wxCommandEvent& event).
Un aspect pe care eu l-am trecut cu vederea. Toate thread-urile in toate exemplele erau membri ai clasei frame, in cazul meu ar fi test2frame. Asta tocmai pentru a nu se pierde pointerul catre thread.
Multumesc de informatii. Diseara cand ajung acasa fac modificarile de rigoare si vedem ce iese. :D
Ar trebuii sa pun intrebari mai des, invat mai repede :))

Re: Problema in utilizare wxThread.

Posted: 22 Mar 2012, 18:21
by bu7ch3r
Pai fa-l membru al clasei, pune-l pe NULL in constructor si sterge-l in destructor...o sa ai o alta problema daca-l stergi cand inca merge :) ...in cazul asta ai putea sa-l astepti.

L.E. Sau sa-l stergi cand se executa semnalul ala "onthreadfinished" sau ceva.

Re: Problema in utilizare wxThread.

Posted: 22 Mar 2012, 19:08
by nedo
Da asta am de gand sa fac.
Creez un pointer catre un workerThread, si in test2Frame creez un membru care defineste, initializeaza un workerThread, si returnez pointer catre acel worker thread.
Ceva de genul

Code: Select all

workerThread* test2Frame::creereThread()
{
    workerThread* thread = new workerThread(m_PathFisierIntrare, m_PathFisierIesire, this)
    thread->Create();
    return thread;
}
Iar in

Code: Select all

void test2Frame::OnbCopiazaClick(wxCommandEvent& event)
{
    bCopiaza->Enable(false);
    bSelFisIes->Enable(false);
    bSelFisInt->Enable(false);
    //m_CritSec.Enter();
    workerThread thread(m_PathFisierIntrare, m_PathFisierIesire, this);
    thread.Create();
    thread.Run();
    //m_CritSec.Leave();

}
O modific in

Code: Select all

void test2Frame::OnbCopiazaClick(wxCommandEvent& event)
{
    bCopiaza->Enable(false);
    bSelFisIes->Enable(false);
    bSelFisInt->Enable(false);
   m_thread = creereThread();
   m_thread->Run(); 
}

Re: Problema in utilizare wxThread.

Posted: 22 Mar 2012, 21:25
by bu7ch3r
Frumos:) poti sa faci si: delete m_thread cand ai chef;))
Poti sa faci direct m_thread = new workerthread.

Re: Problema in utilizare wxThread.

Posted: 22 Mar 2012, 22:10
by nedo
delete m_thread mi se pare cel mai logic sa fac in onThreadFinished. Nu vad vreun motiv sa fac in alta parte.

le. Multumesc foarte mult pentru ajutor.

Re: Problema in utilizare wxThread.

Posted: 22 Mar 2012, 23:54
by nedo
Am revenit cu o adaugire. Nu am nevoie sa folosesc delete m_thread deoarece wxThread se creeaza by default detachable, ceea ce inseamna ca la terminarea executiei face curatenie dupa el insusi, stergandu-se.Referinta. Asta explica de ce imi dadea segmentation fault cand incercam sa fac delete pe thread sau sa ii asignez valoarea de null.