What that's mean.. Basically it is very simple.
If you have an object for example a scene node it can't be grabbed/dropped from multiple threads, an atomic counter is needed for that but irrlicht does not have any. Because It is not needed. Multithreading can be EASILY used for long tasks like file loading, music streaming, network connections etc. Before doing MT you must read some MT tutorials and most important you must understand what happens at Hardware level.
Here's a simple example: we have 2 irrlicht devices and no Multithreading (since i'm using C::B This has been tested only with OpenGL).
Irrlicht version 1.7.2:
The following example won't work for several reasons. The best thing that can happen is to see a "relese of dc failed" message on the console. On worst case you can even get a blue screen. (happened few times when doing experiments with MultiThreading).
Actually i think that irrlicht is missing a "wglMakeCurrent" call in the "beginScene" method. (not sure eh!?)
Code: Select all
#include <irrlicht.h>
using namespace irr;
int main()
{
IrrlichtDevice *dev1 = createDevice(video::EDT_OPENGL); //standard window of size 640x480
IrrlichtDevice *dev2 = createDevice(video::EDT_OPENGL); //standard window of size 640x480
video::IVideoDriver *driver1 = dev1->getVideoDriver();
video::IVideoDriver *driver2 = dev2->getVideoDriver();
while(dev1->run()||dev2->run())
{
if(dev1->isWindowActive())
{
driver1->beginScene();
driver1->endScene();
}
if(dev2->isWindowActive())
{
driver2->beginScene();
driver2->endScene();
}
}
dev1->drop();
dev2->drop();
return 0;
}
Now lets run between different threads:
Code: Select all
#include <myThreadLibrary.h>
#include <irrlicht.h>
using namespace irr;
//Mutex RenderMutex; //global mutex to grant NON-Multithreaded rendering
class myThread: public Runnable
{
public:
virtual void run()
{
IrrlichtDevice *dev = createDevice(video::EDT_OPENGL); //standard window of size 640x480
video::IVideoDriver *driver = dev1->getVideoDriver();
while(dev->run())
{
//RenderMutex.lock();
driver->beginScene();
driver->endScene();
//RenderMutex.unlock();
Sleep(1); //wait 1 millisecond. Go to the next thread
}
dev->drop();
return;
}
}
int main()
{
myThread ThreadA("Foo"); //start a thread named "foo"
myThread ThreadB("Bar"); //start a thread named "bar"
ThreadA.launch();
ThreadB.launch();
ThreadA.wait(); //wait the end of the thread.. some libraries uses "join()" instead of "wait"
ThreadB.wait();
return 0;
}
What is wrong?
Code: Select all
int main()
{
myThread ThreadA();
ThreadA.launch("foo");
ThreadB.launch("bar");
ThreadA.wait(); //wait the end of the thread.. some libraries uses "join()" instead of "wait"
ThreadB.wait();
return 0;
}
Code: Select all
class myThread: public Runnable
{
private:
IrrlichtDevice *dev; //DANGER!!!!
public:
virtual void run()
{
dev = createDevice(video::EDT_OPENGL); //standard window of size 640x480
video::IVideoDriver *driver = dev1->getVideoDriver();
while(dev->run())
{
driver->beginScene();
driver->endScene();
}
dev->drop();
return;
}
}
(and since there's no mutex for access this variable it is also not thread safe).
You need to use a ThreadLocal Storage instead (there are plenty of good tutorials about them).
The easy wat to prevent TLS to being used is so to use different objects for every thread (creating 1 thread object can take some time, on my machine every thread takes 0,5 seconds of startup time).
But be carefull. Static member variables are not thread safe. (and also static methods in general, unless they are not using any member variables).
WARNING: when I use OpenGL and window's windows (lol) I need to lock a global mutex before using the irrlicht equivalent of "beginScene" (wich is in my case clearing color buffers etc.). And i need to unlock the mutex after the irrlicht equivalent of "endScene" (glFlush in mycase).
I commented it since I found out that in Irrlicht this is not needed (on my machine at least). Don't know why, but the Mutex is instead needed if you don't use irrlicht devices as windows! (IMHO i think that using the dll for starting an irrlicht process is handled by the Operative System as a critical section.. not sure about that eh ).
Infact you can start N irrlicht games (tried to open all the executables in the irrlicht examples directory and all runned without problems (the only problem was the low framerate XD) ... of course there can be still problem when two different applications tries to access the same file).
Anway be aware of threads. Use them only if you need them (So when using a thread can give a PROGRAMMING ADVANTAGE, so when you have networking issues, background filestreaming etc..), else they will just add more problems. If you want threads for improving perfomance you must learn to use parallel processing tecniques and forget to use MT for rendering. (only recent hardware and video drivers can do MT also with the rendering but i'm not sure how its happens. But doing that usually requires some times, wich is undesired if you want to make just a little game(so when you tipically have simple design problems to get solved) ).
More topics in this forum related to MT:
http://irrlicht.sourceforge.net/forum/v ... or#p247040 (Front warrior Grass generator)
UPDATE 2:
What happens at Hardware level? Understanding this, can help a lot for multithreaded programming. First of all you must know that C++ is not a language born for concurrent programming (MultiThreading) as Java, so you need to use external libraries, and Language sintax not always help you to do concurrency. There are lots of things that can be sad about MT. How caching works , how RAM works, how the PC works. A general rule when using MT is "RESOURCE ACCESS". You can't generally access the same resource from 2 threads at the same time. (this is a strong assumption, usually is possible multiple access, but only for reading, not for writing). So you cannot write the same data while someone else is writing in the same memory location. That's why for example ReferenceCounting is not thread safe. Because Grabbing and dropping objects will at least write something in the "ReferenceCount" variable, and sometimes 1 thread can still try to call a method on an object wich has just been deleted from another thread after a "drop" call.
There are further rules you can use for improve safety of your code. Using Read/Write locks (with moderation!) can help to avoid conflicts. Try to keep your code simple. When you use Third parts code, you never know where there can be a conflict (for example in irrlicht you can have many SceneManagers, but only 1 mesh cache!).
So things are hard to handle when you use code written by others (Also some libraries that are declared to be Thread safe are not good since the amount of Mutex's Lock is so high that you game performance is slowed down). Thread safety so is not always a good deal.
Loading images in background with irrlicht: COMING SOON (cleaning the code actually )
Code: Select all