multi-threading is very simple: multiple execution position pointers are walking the code at any given time. This means that a routine can be at statement 10 for thread A but at statement 1 for thread B.
This is not harmful, it works well. The ONLY problem there is is that multiple threads have access to the same variables: member variables (local variables in methods are allocated on the stack, so each thread has its own copy, but BEWARE! in the case of a reference to an object, they both have a copy of the REFERENCE, i.e.: both point to the same object!). So if you have a method M which modifies a given member variable, and thread A and B are both executing M, then both can change that member variable and will do so DURING execution of M.
So to make sure a section of code is executed by just 1 thread, you mark it as a 'critical section'. This means that just 1 thread may enter/be in that section at any given time. You do that by placing mutexes in front of the section, or locks: a thread may only proceed if it is granted the mutex/lock, otherwise it has to wait for its turn.
So you FIRST have to determine what the critical sections of your code are: these are sections which have to be atomic. Dull example:
_isValid = CheckIfIsValid(someVar); // A
if(_isValid && someBoolean) // B
{
// do things
}
Lines A and B are part of the single atomic action. You can't see them apart from eachother, because what if thread X executes line A, then the thread Y is given execution time, it runs also line A and _isValid gets a different meaning, after that X is again executing the code and B will not be correct.
So any manipulation of member variables and control flow based on the results of that manipulation are part of the same atomic action. You then have to lock that section. For the rest, it's no big deal.
Locking is done by defining a local object variable of type object, and to lock that. That's the most efficient way.
Often you don't need multi-threading however. Multithreading is only useful (in 99% of the cases) for spawning worker threads doing work while you're waiting for the results and have to be responsive to messages/data from the outside.