Multiple threads



  • On 21/05/2014 at 03:26, xxxxxxxx wrote:

    User Information:
    Cinema 4D Version:   R15 
    Platform:   Windows  ;   
    Language(s) :     C++  ;

    ---------
    I took ScottA's thread example and added a second thread to it.
    So, now I can start 2 threads and it looks good. Two progress counters.

    However, on my Windows 7 with an i5 processor using the task manager, I do not see that extra threads are used when starting the second thread.
    The first thread starts up 2 threads, but starting the 2nd, I do not see any significant increase of processor activity / threads.

    Can I see which cinema 4d thread using which processor thread?
    Do I need to do something extra, to enable cinema to use all threads?
    E.g. do I need to use "class MPThreadPool"?
    If so, is there an example I can learn from?

    Some notes how I added the second thread in ScottA's example.

    class MyDialog : public GeDialog
    {
        private:
            MyThread th;
            MyThread th2;	//second thread
      
    ...
      
    	    case standardBtn2:       //The (regular style) start thread button
                    th2.Start();
    ...
    

    -Pim



  • On 23/05/2014 at 02:17, xxxxxxxx wrote:

    I guess nobody used "class MPThreadPool" before?



  • On 23/05/2014 at 12:02, xxxxxxxx wrote:

    I don't know the referenced code you took your code from but an implementation example for multithreading is given in the sdk docs. Check out the MPThreadPool page.



  • On 26/05/2014 at 04:04, xxxxxxxx wrote:

    Thanks. I see it is a very basic example.
    Some questions, I hope you can help me.

    I want to define 2 threads each processing half of the data.
    How to pass on data to the threads.
    Should I use global data, or can I parameters?
    So instead of virtual void Main(void)for example virtual void Main(dataArray& myBaseArray)

    The documentation tells "MP threads should test for [TestBreak()](file:///D:/Users/pgrooff/Documents/pim/c4d/c++%20R15%20sdk/help/pages/c4d_basedraw/class_BaseDraw68.html#testbreak87)". But it seems that is only for BaseDraw. How to test for a keyboard Escape for example?

    Sorry for these beginner questions. I hope you can help me.
    I will start with this basic example and see how far I get.

    -Pim



  • On 26/05/2014 at 15:13, xxxxxxxx wrote:

    Pass the data to the constructor of your thread. Make sure its properly synchronized.
    You can only check for keyboard Escape from the main thread. If you have a dialog open,
    use it to either let the user press a button or check for the keyboard Escape there.

    Starting both threads asynchronously should work fine. In case your test already includes
    a synchronization mechanism, make sure its locked/unlocked properly (eg. one mistake could
    be that the lock is locked the for full execution time of the thread).

    I think that notice about MP threads is wrong, unless you get the BaseThread pointer from
    some other place. Afaik, BaseThread::TestBreak() will simply call C4DThread::TestDBreak().

    Best,
    -Niklas



  • On 26/05/2014 at 16:27, xxxxxxxx wrote:

    I use a 'control' thread based on Thread and that starts the mp threads.  You should probably use data structures passed to  your mp threads to pass information to each thread.  My Greebler plugin uses threads to greeble polygonal objects by dividing the polygons up between threads.  In my case, the processes are isolated so as to avoid the need for semaphore locking/unlocking required for synchronization.  I will see if I have any 'example' code or can cobble together something like that for you when I have time.



  • On 27/05/2014 at 02:11, xxxxxxxx wrote:

    Originally posted by xxxxxxxx

    I will see if I have any 'example' code or can cobble together something like that for you when I have time.

    Yes please, that would be very helpful.

    -Pim



  • On 27/05/2014 at 05:20, xxxxxxxx wrote:

    Originally posted by xxxxxxxx

    Afaik, BaseThread::TestBreak() will simply call C4DThread::TestDBreak().

    Yep.



  • On 28/05/2014 at 12:01, xxxxxxxx wrote:

    I managed to get the example working and also put everything in another thread.
    So, cinema 4d responds when the threads are running.

    But how to respond to an esc, how to stop all threads?
    I am using in my main thread ct.End(), but that does not stop the threads.
    I think I need to use C4DThread::TestDBreak(), but where to put it. If I put it in the control thread, it is started initially and only once.

    Here some code

    class ControlThread : public C4DThread
    {
    public:
    	virtual void Main(void);
    	virtual const Char* GetThreadName(void) { return "SDK ControlThread"; }
    	virtual Bool TestDBreak(void);
    };
      
    Bool ControlThread::TestDBreak(void)
    {
    	GePrint("ControlThread::TestDBreak.");
    	return true;
    }
      
    ...
      
    void MyThread::Main()
    {    
    	//ct.Start(THREADMODE_SYNCHRONOUS);	// multiprocessing test
    	ct.Start(THREADMODE_ASYNC);
    	return;
    		 
    }
    Bool MyThread::KillThread()
    {
    	GePrint ("MyThread::KillThread ct.End()");
    	ct.End();
    	return true;
    }
    


  • On 28/05/2014 at 17:25, xxxxxxxx wrote:

    You need to test for a user ESC break in TestDBreak() (manually with GetInputEvent()?).  I don't know anymore as I haven't tested for them in my MP threads for some time.  And on a positive condition, you then End() your threads.

    Using a base thread (now BaseThread with C4DThreads) :

    // Thread.Main
    // - This is the Multi-Processor Control Thread
    //*---------------------------------------------------------------------------*
    void ControlThread::Main()
    //*---------------------------------------------------------------------------*
    {
    	if (!mp.Start(THREADPRIORITY_NORMAL))	return;
    	mp.Wait();
    }
    // ControlThread.Initialize
    //*---------------------------------------------------------------------------*
    Bool ControlThread::Initialize()
    //*---------------------------------------------------------------------------*
    {
    	BaseDocument::Free(doc);
    	AtomArray::Free(marray);
    	AtomArray::Free(aarray);
      
    	// Greebler instance holding stock and custom objects
    	Greebler*	greebler =	GetGreeblerData();
    	if (!greebler)			return ErrPrt("ControlThread.Initialize.greebler");
      
    	// Get this once and for task-separation purposes
    	cpu_count =					GeGetCPUCount();
    	for (LONG i = 0L; i != cpu_count; ++i)
    	{
    		tlist[i] =	&thread[i];
    		thread[i].Initialize(this, greebler->GetStockArray());
    	}
      
    	// Initialize MPThread for later use
    	if (!mp.Init(*this,cpu_count,tlist))	return ErrPrt("ControlThread.Initialize.mp.Init()");
      
    	// For combining results
    	aarray =					AtomArray::Alloc();
    	if (!aarray)				return ErrPrt("ControlThread.Initialize.aarray");
    	marray =					AtomArray::Alloc();
    	if (!marray)				return ErrPrt("ControlThread.Initialize.marray");
    	doc =						BaseDocument::Alloc();
    	if (!doc)					return ErrPrt("ControlThread.Initialize.doc");
    	top	=						BaseObject::Alloc(Onull);
    	if (!top)					return ErrPrt("ControlThread.Initialize.top");
    	doc->InsertObject(top, NULL, NULL, FALSE);
    	mcd.doc =					doc;
    	mcd.op =					top;
    	return TRUE;
    }
    


  • On 29/05/2014 at 04:58, xxxxxxxx wrote:

    Sorry, TestDBreak seems to be triggered only once before the MP threads start.
    It is not triggered when the MP threads are running.
    It does not matter if I put it in the control thread or the MP thread?

    Small update:
    If I  put TestDBreak in the MP thread (like below) it is never triggered.
    If I  put TestDBreak in the control thread it is triggered only once.

    // example code for a menu plugin and multiprocessing
      
    #include "c4d.h"
    #include "c4d_symbols.h"
    #include "c4d_memory_mp.h"
      
    #define ID_PIM 123112	//test plugin id
      
    Bool escape_pressed()
    {
        BaseContainer bc;
        Bool rs = GetInputState(BFM_INPUT_KEYBOARD, KEY_ESC, bc);
        if (rs && bc.GetInt32(BFM_INPUT_VALUE)) 
    	{
    		MessageDialog("Stopped by user.");
    		GePrint("Stopped by user.");
    		return true;
    	}
        return false;
    } // end escape_pressed
      
    class MPTest : public C4DThread
    {
    public:
    	Random rnd;
    	Int32	 start;
    	Float	 result;
    	Float	 duration;
      
    	virtual void Main(void);
    	virtual const Char* GetThreadName(void) { return "SDK MpTest"; }
      
    	virtual Bool TestDBreak(void);
    };
      
    Bool MPTest::TestDBreak(void)
    {
    	GePrint("MPTest::TestDBreak: esc? " + escape_pressed());
    	return true;
    }
      
    void MPTest::Main(void)
    {
        // ... See example
    }
      
    class ControlThread : public C4DThread
    {
    public:
    	virtual void Main(void);
    	virtual const Char* GetThreadName(void) { return "SDK ControlThread"; }
    	//virtual Bool TestDBreak(void);
    };
      
    //Bool ControlThread::TestDBreak(void)
    //{
    //	GePrint("ControlThread::TestDBreak: esc? " + escape_pressed());
    //	return true;
    //}
      
    void ControlThread::Main(void)
    {
    	GeShowMouse(MOUSE_BUSY);
      
    	MPThreadPool mp;
    	Int32				 i, cnt = GeGetCPUCount();
      
    	GePrint("Nr of processors / cores: " + String::IntToString(cnt));
    	if (cnt == 0)
    		return;
    	MPAlloc<MPTest>				 thread;
    	AutoGeFree<C4DThread*> list = NewMemClear(C4DThread *, cnt);
    	if (!list || !thread.Init(cnt))
    		return;
      
    	for (i = 0; i < cnt; i++)
    	{
    		thread[i].start	 = i;
    		thread[i].result = 0.0;
    		list[i] = &thread[i];
    	}
      
    	if (!mp.Init(*this, cnt, list))
    		return;
    	Float64 start_time = GeGetMilliSeconds();
    	if (!mp.Start(THREADPRIORITY_BELOW))
    		return;
    	Float64	start_duration = GeGetMilliSeconds() - start_time;
      
    	mp.Wait();
      
        //... set string See example
      
    	GeShowMouse(MOUSE_NORMAL);
    	MessageDialog(str);
    }
      
    class MenuTest : public CommandData
    {
    public:
    	virtual Bool Execute(BaseDocument* doc);
    };
      
    Bool MenuTest::Execute(BaseDocument* doc)
    {
    	ControlThread ct;
    	return ct.Start(THREADMODE_SYNCHRONOUS);	// multiprocessing test
    }
      
    Bool RegisterMyPlugin(void)
    {
        // ...
    }
      
    
    


  • On 31/05/2014 at 07:39, xxxxxxxx wrote:

    I tried various ways, but still I do not know how to stop the multi threads or where to put TestDBreak.
    I'm getting no response no matter where I put mp.End()?



  • On 03/06/2014 at 02:19, xxxxxxxx wrote:

    I'm starting to pull my hair out of my head.
    Still can't get it to work.

    All help is appreciated!



  • On 03/06/2014 at 05:42, xxxxxxxx wrote:

    Ok, I solves it using a workaround (as in ScottA's example).
    Still doubt about .End(), but this workaround works for me.

    -Pim


Log in to reply