2012-04-06 10 views
0

Mein Problem ist, dass ich zwei TThread installiert habe (von Borland C++ VCL). Beide Konstruktoren sind erfolgreich. Aber nur der erste TThread wird ausgeführt.Zwei TThreads erfolgreich konstruiert, aber nur der erste wird ausgeführt

(Das Ziel dieses Codes ist das Laden von etwa 100 PNG-Bilddateien in eine Liste von Texturobjekten; diese Texturobjekte (TMyObject) haben eine "LoadFromFile" -Funktion, die etwa 60 Ticks dauert).

Ich habe viele Erläuterungen zu multithreading geblättert, und haben daher:

=> kein Erfolg in all diesen Versuchen.

Hier unten ist mein Code, den ich versuchte zu vereinfachen. Jede Hilfe oder Erklärung würde helfen, meinen zweiten Thread auszuführen.

//--------------------------------------------------------------------------- 
class TMainClass 
{ 
private: 
    TMyList<SmartPtr<TEvent> > mEventList; 
    SmartPtr<TMyThread> mThread1, mThread2; 
    int mCount; 
protected: 
    int mCurrent, mLast; 
    TMyList<SmartPtr<TMyObject> > mObjectList; 
    TMyObject *mpObject;  
    void MyInit(); 
public: 
    TMainObject(TMyParentObject *parent); 
    virtual ~TMainObject(); 
    virtual void PeriodicTask(); 
}; 

//--------------------------------------------------------------------------- 
class TMyThread : public TThread 
{ 
    TMyList<SmartPtr<TEvent> > *mpEventList; 
    TMyList<SmartPtr<TMyObject> > *mpObjectList; 
    int mStart, mEnd; 
public: 
    TMyThread( TMyList<SmartPtr<TEvent> > *pEventList, 
       TMyList<SmartPtr<TMyObject> > *pObjectList, 
       int Start, int End); 
    virtual void __fastcall Execute(void); 
}; 

//--------------------------------------------------------------------------- 
//--------------------------------------------------------------------------- 
//--------------------------------------------------------------------------- 
TMainClass::TMainClass(TMyParentObject *parent) 
{  
    mCount = 0; 
} 

TMainClass::~TMainClass() 
{ 
    if (mThread1.GetPtr() != NULL) 
    { 
     mThread1->Terminate(); 
     mThread1 = SmartPtr<TMyThread> (NULL); 
    } 
    if (mThread2.GetPtr() != NULL) 
    { 
     mThread2->Terminate(); 
     mThread2 = SmartPtr<TMyThread> (NULL); 
    } 
    mpObject = NULL; 
    mObjectList.Clear(); 
    mEventList.Clear(); 
} 

void TMainClass::MyInit() 
{ 
    if (mThread1.GetPtr() != NULL) return;  
    mObjectList.Clear(); 
    mEventList.Clear(); 
    mCount = GetNumberOfFiles("C:/MyPath/");  
    for (int i = 1; i <= mCount; i++) 
    { 
     SmartPtr<TEvent> lEvent (new TEvent(NULL, false, false, "")); 
     lEvent.GetPtr()->ResetEvent(); 
     mEventList.Add(lEvent); 
    } 
    mThread1 = SmartPtr<TMyThread> (new TMyThread(&mEventList, &mObjectList, 1,  floor(mCount/2.0))); // lock before that ? 
    mThread2 = SmartPtr<TMyThread> (new TMyThread(&mEventList, &mObjectList, floor(mCount/2.0)+1, mCount)); // lock before that ? 

    mCurrent = 0; 
} 

void TMainClass::PeriodicTask() 
{ 
    mpObject = NULL; 
    int lCount = mObjectList.Count(); 
    if (lCount != 0) 
    { 
     ++mCurrent;   
     mCurrent = min(mCurrent, lCount); 
     if ( mLast != mCurrent 
      && mEventList[mCurrent]->WaitFor(120) != wrSignaled ) 
      return; 
     mLast = mCurrent; 
     mpObject = mObjectList[mCurrent].GetPtr(); // lock before that ? 
    } 
    if (mpObject == NULL) return; 

    mpObject->MyObjectUtilisation(); // lock before that ? 
} 

//--------------------------------------------------------------------------- 
TMyThread::TMyThread( TMyList<SmartPtr<TEvent> > *pEventList, TMyList<SmartPtr<TMyObject> > *pObjectList, 
         int Start, int End); 
:TThread(false) 
{ 
    mpEventList = pEventList; // lock before that ? 
    mpObjectList = pObjectList; // lock before that ? 

    mStart = Start; 
    mEnd = End; 

    FreeOnTerminate = false; 
} 

void __fastcall TMyThread::Execute(void) 
{ 
    for (int i = mStart; i <= mEnd; i++) 
    { 
     try 
     { 
      if (mpEventList != NULL && mpObjectList != NULL) 
      {     
       SmartPtr<TMyObject> pObject (new TMyObject()); 
       pObject->LoadFromFile(i); 
       // common memory accesses before which I want to put a lock 
       mpObjectList->Insert(i,pObject); 
       mpEventList[i]->SetEvent(); 
       // place where I could release this lock 
      } 
     } 
     catch(Exception &e) 
     { 
      ShowMessage("Exception in Execute : " + e.Message); 
     } 
    } 
    return; 
} 

Cheers, Arnaud.

+0

Sind Sie sicher, beide Threads haben ihre 'Execute' Methode aufgerufen? Können Sie den Code in einem Debugger durchgehen, um zu sehen, was passiert? –

+0

Kann jede Thread-Instanz auch korrekt ausgeführt werden, wenn Sie die Erstellung der anderen Instanz auskommentieren? Für Thread 2 haben 'mStart' und' mEnd' sinnvolle Werte? –

+0

Nun, ich werde Ihre Texture-Objekte nicht neu entwickeln, also muss ich mit etwas anderem auskommen, das png-Dateien laden kann. TPngImage scheint eine gute Wette zu sein? –

Antwort

0

OK, ich habe ein Beispiel für einen PNG-Lader mit Threads (eigentlich ein Threadpool). Es unterscheidet sich geringfügig von Ihrem Design, aber es funktioniert OK. Ich habe versucht, den ganzen Code in den Header zu bekommen, um es einfacher zu machen, aber die Abhängigkeiten erlaubten es nicht und so musste ich auch etwas cpp haben. Ich habe ein Testformular erstellt, das eine Diashow anzeigt, also habe ich auch den Code dafür eingefügt.

// HPP-Header:

#ifndef PNGloaderH 
#define PNGloaderH 

#include <Classes.hpp> 
#include <deque.h> 
#include <vector.h> 
#include <PngImage.hpp> 
#include <usefulStuff.hpp> 

class CBthreadPool; 

// Task class to inherit from, supplying the abstract run() method. 
class CBtask { 
    friend class CBthreadPool; 
    friend class TpoolThread; 
    CBthreadPool *myPool; 
    TNotifyEvent FonComplete; 
protected: 
    int param; 
    virtual void DoCompleted(){ // called after run, normall calls OnComplete 
     if (FonComplete!=NULL){FonComplete((TObject*)this);}; 
     delete(this); 
    } 
public: 
    String errorMess; 
    CBtask(int inParam, TNotifyEvent OnComplete){ // user param and 
     FonComplete=OnComplete; // an OnComplete callback to be called after run() 
     param=inParam; 
    }; 
    virtual void run()=0; 
    void submit(CBtask *aTask); 
}; 

// Producer-Consumer queue for tasks 
class CBSqueue{ 
    TCriticalSection *access; 
    deque<CBtask*> workQueue; 
    TSemaphore *queueSema; 
public: 
    CBSqueue(){ 
     access=new TCriticalSection(); 
     queueSema=new TSemaphore(NULL,0,MAXINT,NULL,false); 
    }; 
    void push(CBtask *task){ 
     access->Acquire(); 
     workQueue.push_front(task); 
     access->Release(); 
     queueSema->Release(); 
    }; 
    bool pop(CBtask **task,DWORD timeout){ 
     if(wrSignaled==queueSema->WaitFor(timeout)){ 
      access->Acquire(); 
      *task=workQueue.back(); 
      workQueue.pop_back(); 
      access->Release(); 
      return(true); 
     } else return false; 
    }; 
}; 

// Threadpool thread 
class TpoolThread : public TThread{ 
    CBthreadPool *FmyPool; 
protected: 
    virtual void __fastcall Execute(void); 
public: 
    TpoolThread(CBthreadPool *myPool):TThread(true){ 
     FmyPool=myPool; 
     Resume(); 
    }; 
}; 

// General purpose threadpool 
class CBthreadPool { 
    friend class TpoolThread; 
    int threadCnt; 
    CBSqueue taskQueue; // P-C queue of CBtask, (or descendants) 
public: 
    CBthreadPool(int numThreads){ 
     for(threadCnt=0;threadCnt<numThreads;threadCnt++){new TpoolThread(this);} 
    } // crate all the thradpool threads, passing them the queue to wait on 
    void submit(CBtask *aTask){ // method to submit work 
     aTask->myPool=this; // in case the task wants to issue any tasks 
     taskQueue.push(aTask); // off it goes.. 
    }; 
}; 

/* This CBtask descendant has a 'run' method that loads 'thePNG' object 
from a filespec passed in ctor. Instances of this class are queued to the 
thread pool to load the PNG files */ 
class PNGtask:public CBtask{ 
    String Ffolder; 
public: 
    TPngImage *PngImage; 
    void run(){ 
     PngImage=new TPngImage; 
     PngImage->LoadFromFile(Ffolder); 
    }; 
    PNGtask(String dir,TNotifyEvent OnDone):CBtask(0,OnDone){Ffolder=dir;}; 
}; 

/* This CBtask descendant has a 'run' method that iterates a folder passed in 
the ctor for 'PNG' files, submits a PNGtask for each one and waits for the last 
to complete. One is issued to the pool for each folder to be searched */ 
class PNGfilesTask:public CBtask{ 
    String Ffolder; 
    int taskCounter; 
    TEvent *CompleteEvent; 
    TCriticalSection *counterLock; 
public: 
    vector<TPngImage*> *PngImages; 
    TObject *userData; 
    PNGfilesTask(String folder, TNotifyEvent OnComplete, 
       vector<TPngImage*> *PngImages,TObject *UserData):CBtask(0,OnComplete){ 
     Ffolder=folder; 
     this->userData=UserData; 
     CompleteEvent=new TEvent(NULL,false,false,"",false); 
     counterLock=new TCriticalSection(); 
     this->PngImages=PngImages; 
    } 
    void run(){ // get all the file names 
     TStringList *files=listAllFilesInThisFolderMatching(Ffolder,"*.png"); 
     taskCounter=files->Count; // set the task counter 
     for (int i=0; i<files->Count; i++) { // submit a PNGtask for each file 
      PNGtask *aTask=new(PNGtask)(Ffolder+"\\"+files->Strings[i],OnFilesLoaded); 
      submit(aTask); 
     } 
     delete(files); 
     CompleteEvent->WaitFor(INFINITE); // and wait for the last one to finish 
    }; 
    void __fastcall OnFilesLoaded(TObject *Sender){ // called by each PNGtask 
     counterLock->Acquire();      // thread-safe 
     PngImages->push_back(((PNGtask*)Sender)->PngImage); // PNGImage into vector 
     if(--taskCounter==0){ // all loads done? 
      CompleteEvent->SetEvent(); // signal the run() method to continue 
     }; 
     counterLock->Release(); 
    }; 
}; 

/* This class inherits from threadpool and has a 'PNGget' method that takes a 
folder path, a pointer to a caller-supplied vector that will be loaded with 
TPngImage* instances, an 'OnComplete' callback and a user context object. 
When the files are all loaded, the 'OnComplete' event is called with the 
PNGfilesTask object as the Sender. */ 
class PNGload:public CBthreadPool{ 
public: 
    PNGload(int numThreads):CBthreadPool(numThreads){ 
    } 
    void PNGget(String folder,vector<TPngImage*> *results, 
       TNotifyEvent OnComplete, TObject *userData){ 
     PNGfilesTask *loadPNG=new(PNGfilesTask)(folder,OnComplete,results,userData); 
     submit(loadPNG); // execute loadPNG->run() on the threadpool 
    }; 
}; 

#endif 

// CPP

#include "PNGloader.hpp" 

void __fastcall TpoolThread::Execute(){ 
    CBtask *thisTask; 
    while(FmyPool->taskQueue.pop(&thisTask,INFINITE)){ 
     try{ 
      if(thisTask!=NULL){ 
       thisTask->errorMess=""; 
       thisTask->run(); 
      } 
      else{ 
       FmyPool->taskQueue.push(thisTask); 
       exit; 
     } 
     } 
     catch(exception& e){ 
      thisTask->errorMess=e.what(); 
     } 
     thisTask->DoCompleted(); 
     thisTask; 
    } 
}; 

void CBtask::submit(CBtask *aTask){ 
    myPool->submit(aTask); 
} 

// Testform Datei - ermöglicht es dem Benutzer, einen Ordner auszuwählen und dann eine Dia-Show von jedem PNG Dateien innerhalb. Es hat ein Panel an der Spitze für eine Schaltfläche und ein Label, ein Timer und der untere Teil des Formulars ist alles ein TImage.

//--------------------------------------------------------------------------- 

#include <vcl.h> 
#pragma hdrstop 

#include "PNGform.h" 
//--------------------------------------------------------------------------- 
#pragma package(smart_init) 
#pragma resource "*.dfm" 
TfoPNGload *foPNGload; 
//--------------------------------------------------------------------------- 
__fastcall TfoPNGload::TfoPNGload(TComponent* Owner) 
    : TForm(Owner) 
{ 
    myLoad=new PNGload(10); 
} 
//--------------------------------------------------------------------------- 

void __fastcall TfoPNGload::SpeedButton1Click(TObject *Sender) 
{ 
     if(PathName==""){PathName="C:\\";}; 
     if (SelectDirectory("Select Directory",PathName,PathName)){ 
      myLoad->PNGget(PathName,new(vector<TPngImage*>),filesLoaded,NULL); 
     } 
} 
//--------------------------------------------------------------------------- 

void __fastcall TfoPNGload::filesLoaded(TObject *Sender){ 
    vector<TPngImage*> *PngImages; 
    PngImages=((PNGfilesTask*)Sender)->PngImages; 
    PostMessage(Handle,WM_APP,0,long(PngImages)); 
}; 

void __fastcall TfoPNGload::WMAPP(TMessage& msg){ 
    vector<TPngImage*> *thisVectorPtr=(vector<TPngImage*>*)msg.LParam; 
    resultList.push_back(thisVectorPtr); 
    vecSize=thisVectorPtr->size(); 
    if (vecSize>0) tiSlideShow->Enabled=true; 
}; 


void __fastcall TfoPNGload::getNextVector(){ 
    if (resultList.size()!=0) { 
     currentPNG=resultList.back(); 
     resultList.pop_back(); 
     currentPNGindex=0; 
    } 
    else 
     currentPNG=NULL; 
}; 

void __fastcall TfoPNGload::tiSlideShowTimer(TObject *Sender) 
{ 
    if(currentPNG==NULL) getNextVector(); 
    else 
     if (currentPNG->size()==0) { 
      delete(currentPNG); 
      getNextVector(); 
     } 
    if(currentPNG==NULL) { 
     tiSlideShow->Enabled=false; 
     Label1->Caption="No files left"; 
     return; 
    }; 
    TPngImage *thisPNG; 
    Label1->Caption=IntToStr((int)currentPNG->size())+" PNG files left"; 
    thisPNG=currentPNG->back(); 
    currentPNG->pop_back(); 
    Image1->Picture->Assign(thisPNG); 
    delete(thisPNG); 
} 
//--------------------------------------------------------------------------- 

// Formularkopf

//--------------------------------------------------------------------------- 

#ifndef PNGformH 
#define PNGformH 
//--------------------------------------------------------------------------- 
#include <Classes.hpp> 
#include <Controls.hpp> 
#include <StdCtrls.hpp> 
#include <Forms.hpp> 
#include <Buttons.hpp> 
#include <Dialogs.hpp> 
#include <ExtCtrls.hpp> 
#include <PngImage.hpp> 
#include "Pngloader.hpp" 
#include "PNGform.h" 

//--------------------------------------------------------------------------- 
class TfoPNGload : public TForm 
{ 
__published: // IDE-managed Components 
    TImage *Image1; 
    TPanel *Panel1; 
    TSpeedButton *SpeedButton1; 
    TTimer *tiSlideShow; 
    TLabel *Label1; 
    void __fastcall tiSlideShowTimer(TObject *Sender); 
    void __fastcall SpeedButton1Click(TObject *Sender); 
private: 
    PNGload *myLoad; 
    String PathName; 
    void __fastcall filesLoaded(TObject *Sender); 
    void __fastcall getNextVector(); 
protected: 
    MESSAGE void __fastcall WMAPP(TMessage& msg); 
public:  // User declarations 
    __fastcall TfoPNGload(TComponent* Owner); 

    BEGIN_MESSAGE_MAP 
    MESSAGE_HANDLER(WM_APP, TMessage, WMAPP) 
    END_MESSAGE_MAP(TForm) 

    vector < vector<TPngImage*>* > resultList; 
    vector<TPngImage*> *currentPNG; 
    int currentPNGindex; 
    int vecSize; 
}; 
//--------------------------------------------------------------------------- 
extern PACKAGE TfoPNGload *foPNGload; 
//--------------------------------------------------------------------------- 
#endif 

Test form

+0

Vielen Dank für diese Antwort. Ich habe mein Ziel vorübergehend mit zwei getrennten Listenpaaren erreicht. Aber ich werde versuchen, Ihre Erklärung so schnell wie möglich zu verstehen. Nochmals vielen Dank im Voraus. – Arnaud

Verwandte Themen