Multi-threading using the IThread class

From EDM2
Jump to: navigation, search

Written by Gordon Zeglinski

Introduction

This issue's OOPS Avenue is in response to a reader's email. Things are extremely hectic, so sometimes email doesn't get answered as promptly as I would like it to be. Keep the mail coming, but please be patient; I'll answer it as soon as I can.

In this issue, we will look at multi-threading using the IThread class. The IThread class is part of the ICLUI shipped with C-Set++ 2.0 and 2.1; however, we will be building upon the drag and drop example of last issue, which requires C-Set++ 2.1.

The IThread Class

The IThread class is used to manipulate threads in the ICLUI. An instance of IThread and the thread it represents are not bound together. If the thread terminates, the instance is not destroyed. If the instance is destroyed, the thread continues executing.

Starting a Thread with IThread

There are two methods that can be used to start a thread. The first, is to use the one of the constructors that take a function address or a reference to an instance of IThreadFn as an argument. The second is to use the "start member" function. We will look at both methods here.

The following code snippet illustrates both methods of starting a thread. In this example, we use a member function of the class foo as the threads starting function.

class foo{

public:
   foo();

   void fooThread();

};

void main(){
   //Create an instance of the foo class
   foo fooInst;

   // create the member thread dispatch object
   IThreadMemberFn<foo> ThreadFnc(fooInst,&foo::fooThread);

   // create a reference to the member thread dispatch object
   IReference<IThreadFn> ThreadFncRef(&ThreadFnc);

   // create a thread object and start the thread
   IThread ImmediateDispatch(ThreadFncRef);

   // create a thread object
   IThread DelayedDispatch;

   /*
   Do some work here
   */

   DelayedDispatch.start(ThreadFncRef);
}

The class IThreadMemberFn<class T> holds both an instance of a class and a pointer to a member function of that class. These two pieces of information are need to start the thread using the correct member function and instance. Note, that since IThreadMemberFn is a template class, it can be used with any class.

In this example, we only looked at starting threads on member functions. The method of starting threads on non-member functions is almost identical to the one outlined above. Thus, it is left up to the reader to explore this further.

Building a Simple Test Application

This section should look familiar to those who have read the last issue. We will take our application from last issue, and move the file loading routine into a separate thread. In this section, we will look at only the modifications made to last issues code that are needed to accomplish our goal here.

The class definition for MyFrame has been expanded to allow multi-threaded loading of files into the MLE.

class MyFrame:public IFrameWindow{
public:
   MyFrame(const char *Title);
   void StartLoadThread();         //Added to start thread

   void SetFileName(IString &S){FileName=S;}
			   //Added to set the filename protected:
   void LoadFile();        //Added: load thread start point


   // New members to support multi-threaded loading
   IReference<IThreadFn>    ThreadFncRef;
   IThreadMemberFn<MyFrame> *ExecuteThreadFnc;
   IThread                  ExecuteThread;

   IString FileName;

   IMultiLineEdit EditWin;
   AFileProvider FileProvider;
};

Added to the version of MyFrame shown above, are several several new member functions that handle the multi-threaded file loading. The constructor is modified as follows to initialize the new data members.

MyFrame::MyFrame(const char *Title):
   IFrameWindow(Title,IResourceId(1),  //-------------------
      IFrameWindow::titleBar|          //
      IFrameWindow::sizingBorder|      //
      IFrameWindow::minimizeButton|    // Create the Frame
      IFrameWindow::systemMenu|        //  Window
      IFrameWindow::shellPosition|     //
      IFrameWindow::minimizeButton|    //
      IFrameWindow::windowList|        //
      IFrameWindow::maximizeButton),   //--------------------
   EditWin(10,this,this){              // Create the Edit Window
                                       // ID=10, use this frame
                                       // window as the parent and
                                       //owner
                                       //---------------------

   setIcon(IResourceId(1));
   setClient(&EditWin);

   //enable default drag and drop handler
   IDMHandler::enableDragDropFor(&EditWin);
   //attach the provider
   EditWin.setItemProvider(&FileProvider);

   //Create the thread support members.
   ExecuteThreadFnc=new IThreadMemberFn<MyFrame>(*this,
                  &MyFrame::LoadFile);

   ThreadFncRef=IReference<IThreadFn>(ExecuteThreadFnc);

   show();
}

The targetDrop routine is modified so that it uses the member functions we added to MyFrame to set the file name and start the thread which will read in the file. In the following code, we use the fact that the parent of the MLE is an instance of MyFrame to get the instance of the MyFrame window and call the thread creation function.

Boolean AFileItem::targetDrop( IDMTargetDropEvent &Event){

   IMultiLineEdit *DropWin=
   	(IMultiLineEdit *)this->targetOperation()->targetWindow();

   IString
    fname = this->containerName() + this->sourceName();

   MyFrame *FrameWin=(MyFrame*) DropWin->parent();

   FrameWin->SetFileName(fname);
   FrameWin->StartLoadThread();

   return true;
}

Now that we've seen how the old code was modified, we look at the two new member functions of MyFrame. These functions don't do anything that we haven't already seen.

void MyFrame::StartLoadThread(){
   ExecuteThread.start(ThreadFncRef);
}

void MyFrame::LoadFile(){
   //erase the edit window
   EditWin.removeAll();

   //load the file into the edit window
   EditWin.importFromFile(FileName);
}

The source code to this issue is packaged the same way as the source code in the previous issue.

Summary

In this issue, we have seen how to use IThread and its supporting classes to create a multi-threaded PM application by building upon our drag and drop example from last issue.