2012-04-06 300 views
0

我的問題是,我實例化了兩個TThread(來自Borland C++ VCL)。 他們的構造函數都成功了。 但只有第一個TThread被執行。 (這個代碼的目標是在紋理對象列表中加載大約100個PNG圖像文件;這些紋理對象(TMyObject)有一個「LoadFromFile」函數,該函數持續大約60個刻度)。兩個TThreads成功構建,但只有第一個被執行

我瀏覽過很多關於多線程的解釋,因此有:

=>在所有這些嘗試沒有成功。

下面是我試圖簡化的代碼。 任何幫助或解釋將有助於我的第二個線程被執行。

//--------------------------------------------------------------------------- 
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; 
} 

乾杯, Arnaud。

+0

你確定這兩個線程都稱他們'Execute'方法是什麼?你能通過調試器中的代碼來看看發生了什麼嗎? – 2012-04-06 17:19:21

+0

另外,如果註釋掉其他實例創建,每個線程實例是否都可以正確運行?對於線程#2,「mStart」和「mEnd」是否有意義的值? – 2012-04-06 17:26:45

+0

好吧,我不打算重新開發你的紋理對象,所以我不得不做一些可以加載PNG文件的其他東西。 TPngImage似乎是一個很好的選擇? – 2012-04-06 20:45:09

回答

0

好吧,我做了一個使用線程的PNG加載器(實際上是一個線程池)的例子。它與你的設計略有不同,但它工作正常。我試圖讓頭文件中的所有代碼更容易發佈,但依賴關係並不允許它,所以我也必須有一點cpp。我創建了一個顯示幻燈片的測試表單,所以我也包含了代碼。

// HPP頭:

#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); 
} 

//測試形式 - 允許用戶選擇文件夾,然後顯示任何PNG 的幻燈片放映裏面的文件。它的頂部有一個按鈕和一個標籤,一個計時器和表格的下半部分都是一個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); 
} 
//--------------------------------------------------------------------------- 

//表頭

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

#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

非常感謝這個答案。我用兩對單獨的名單暫時實現了我的目標。但我會盡快理解你的解釋。再次提前感謝。 – Arnaud 2012-04-11 11:54:13

相關問題