Vasu Balakrishnan’s Blog


leave a comment »

Here is an excellent series of posts about Multithreading. I’ve just started reading the first post. This is a must read.

As I’m reading these posts, I’m going to log my observations.

  • There are four main aspects to a multi threaded application – state, atomicity, serializability, linearizability
  • In Unix, process is the fundamental unit of concurrency, whereas in Windows its the threads. The post says Windows chose this approach because its “lighter” to create processes in Unix than in Windows. I’m not sure what “lighter” means. How Unix Process is architecturally different from a Windows Process?
  • A thread could be either a foreground / background thread and that a background thread doesn’t prevent a process from exiting.
  • ThreadPool provides the application with a pool of worker threads. They are background threads, use default stack size, runs at default priority and is in multithreaded apartment. There is only one thread pool per application.
  • A stack is a contiguous region of memory that grows downward and is used for several operations. The default stack size is 1MB. In C#, you can allocate a block of memory in stack using stackalloc. They are not subject to garbage collection and lifetime is limited to the method in which it is defined.
  • In using Stack as storage post, the author says if you’ve a static field marked as ThreadStatic, you should always check for lazy initialization. The initialization code will run only once, and if we didn’t have lazy initialization the second thread would throw an error. That’s why he has the field wrapped in a static property. Wow!!! I wouldn’t have guessed it.
  • In a multithreaded world, problems happen when you’ve several threads reading and writing or several threads writing to a shared memory location. The author says if we build immutable types, then we don’t need to synchronize access to these items. There are two types of immutable – shallow and deep. A type is shallow immutable if each field of an object never changes during its lifetime. The object is deeply immutable if each field references other objects which don’t change over time too. CLR supports limited immutability via read only fields. There is no CLR support for ensuring deep immutability.
  • Synchronization can be classified into two broad sub areas : data / control. The Data is more commonly used where we ensure that only one thread accesses data at each time. Control is used when one thread depends on other having executed some piece of code.
  • Data Synchronization relies on serializing access to shared state. This is done by creating Critical regions, which delimits several instructions that are executed by a specific thread in a serialized fashion. The usage pattern is simple – thread enters critical region, executes several instructions and leaves the critical region (assuming that there are no exceptions and thread isn’t aborted. I’ve to find out what happens in this scenario)
  • Control Synchronization is appropriate when you need to ensure collaboration between several threads. In this case, we will have threads that will need to wait while some condition isn’t true. There are several ways to implement wait logic
  • busy waiting – you’ll have an infinite cycle which will run until specific condition is true. This might be useful if the thread can do some useful work while waiting. Otherwise, its not a good approach to just keep spinning without any work.
  • real wait – In this case, OS puts the thread in a real wait state so that it wont consume processor time, but will incur thread context switch costs.
  • continuation passing – This relies on passing a closure for being executed when other specific action ends. This might be a good option where waiting is too expensive. Shared state is encapsulated in messages which are passed between threads. This topic is something that I’ve been trying to understand. I didn’t expect it to be used in this context.
  • Kernel objects – We can use kernel objects to achieve Data / Control Synchronization. There are several kernel objects. In traditional Win32, each kernel object is represented by a HANDLE, and can be in one of two states – signaled and non-signaled. Transition between the states depends on the type of kernel object we’re using. We are interested only in waiting on Synchronization kernel objects – mutexes, semaphore, event (auto/manual) and timers. The .NET framework introduces a base class (WaitHandle) on which we can wait, that is extended by mutexes, semaphore and events. AutoResetEvent / ManualResetEvent extends the EventWaitHandle. A thread gets blocked waiting for a kernel object to transition from non-signaled to signaled state, it might wake up without a corresponding transition. This happens if the thread performs so called “alertable wait”. He says in managed code, thread always performs alertable wait. I’m not really sure what they are and to me it seems to defeat the purpose of waiting on a kernel object.
  • Advertisements

    Written by nbvasu

    June 12, 2009 at 2:36 pm

    Posted in C#

    Leave a Reply

    Fill in your details below or click an icon to log in: Logo

    You are commenting using your account. Log Out /  Change )

    Google+ photo

    You are commenting using your Google+ account. Log Out /  Change )

    Twitter picture

    You are commenting using your Twitter account. Log Out /  Change )

    Facebook photo

    You are commenting using your Facebook account. Log Out /  Change )


    Connecting to %s

    %d bloggers like this: