The first, and most important thing to realize about concurrent programming is that it's all about two things: your data and your threads. If you only have one thread, then you don't have to worry about concurrency issues. If you have more than one thread, then you only have to worry about concurrency issues if more than one thread can simultaneously access that data. And that's my first principle of concurrent programming: If your data is never accessed on more than one thread, then you don't have to worry about concurrency. Again, the guys who get concurrency are cringing with this principle- the reality is (of course) more complicated than that, I'll get back to why it's more complicated later (I need to introduce some more concepts beforehand).
In Win32, in general, there are three ways that you can guarantee that your thread is the only one executing the data.
The first is your stack. On Win32, the data on your stack is owned by the thread (this might not be true for other architectures, I don't know :(). Unless you explicitly pass pointers to your stack to another thread, then you don't have to worry about other threads messing with your stack data, so you don't need to worry about protecting the data.
The second way of ensuring that only one thread can access your data is to use ThreadLocalStorage, or TLS. The idea behind TLS is that when your process starts, it allocates a "slot" in TLS. That allocation returns you an index into a table, and you can stick whatever value you want to into that table. When your thread starts up, you can allocate a block of memory, stick it into the table, and then, later on during the execution of the thread, you can go back and query the value of that block. The block remains per-thread, and can be accessed without protecting the data. This allows you to maintain per-thread context blocks which can be used to hold state that's more global than the stack. Btw, the C runtime library allows you to declare variables in TLS by simply decorating them with __declspec(thread) - there are some caveats about using this, but the facility is available...
The third way of ensuring that only one thread can access your data is simply to be careful in how you write your code. As an example, in my last "What's wrong with this code" article, I purposely allocated the FileCopyBlock structures in one thread, put them on a queue and executed them in worker threads. As a result, I didn't have to protect the FileCopyBlock fields - since only one thread could ever access the data at a time, they didn't need to be protected. Now more than one thread accessed the data (the block was constructed on the main thread and destructed on the worker threads). But at any given time, the blocks weren't accessed by more than one thread. This principle can be applied in a number of different ways - my example was quite simple, but it wouldn't be difficult to imagine a FSM where the state was kept in a block that was enqueued and dequeued based on state transitions - the block would only ever be accessed by one thread at a time and thus wouldn't have to be protected.
It turns out that you can write some fairly sophisticated multithreaded code without ever having to ever worry about synchronizing your shared data, just by being careful and setting up your data structures appropriately, you can do pretty amazing things.
But, of course, there are times that you can't avoid having more than one thread accessing your data. Tomorrow, I'll talk about some of the ways around that problem.
Edit: Principal->Principle (thanks Mike :))