0

我的问题是我已经实例化了两个 TThread(来自 Borland C++ VCL)。他们的两个构造函数都成功了。但只有第一个 TThread 被执行。

(此代码的目标是在纹理对象列表中加载大约 100 个 png 图像文件;这些纹理对象 (TMyObject) 具有持续大约 60 个刻度的“LoadFromFile”函数)。

我浏览了很多关于多线程的解释,因此:

=> 在所有这些尝试中都没有成功。

下面是我试图简化的代码。任何帮助或解释都将有助于执行我的第二个线程。

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

干杯,阿诺德。

4

1 回答 1

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

测试表格

于 2012-04-08T23:55:50.833 回答