Thoughts from the office by Ed Ball
Thursday, September 29, 2005

This was a good session by Jan Gray on the fundamentals of using concurrency in software development. Why bother with concurrency? For one, hanging the application is as disruptive as a crash, so it is important to free up the user interface thread by delegating work to other threads. You can also improve the user experience by allowing the user to see the status of the work, examine partial results of the work so far, and cancel the work if necessary.

Another use of concurrency is to increase performance by spreading the work across multiple processors. Of course, measurement is the key here; before adding the complexity of concurrency, make sure that you’re already pegging the CPU, and make sure that your algorithms are already fully optimized.

This session provided a number of tips:

  • Server applications are often already highly concurrent, since each client gets their own thread, and SQL can distribute a single database query among multiple processors.
  • OpenMP exists for doing straightforward parallelism in client applications. (It is supported by Visual C++ 2005.) Otherwise, the classic tools are threads using shared memory protected by locks.
  • The standard techniques for protecting shared memory from conflicts, in increasing order of difficulty, are confinement (sharing as little memory as possible), immutability (not allowing writes to the shared memory), and synchronization (locking access to the shared memory).
  • There are three primary goals when writing concurrent code: safety (identifying and holding “invariants” in shared data), liveness (avoiding deadlocks – two threads waiting for a lock the other holds), and efficiency (making the code as parallel as possible, generally by waiting as little as possible).
  • In most cases, the thread pool is best for doing work in parallel, since it may not have the overhead of creating a new thread. In .NET, use ThreadPool.QueueUserWorkItem or BackgroundWorker.
  • In .NET, use the Monitor class for shared memory management. Two threads interacting via shared memory use Monitor.Enter and Monitor.Exit to protect the memory. If one thread needs to wait for the other, Monitor.Wait releases the lock until the other thread calls Monitor.Pulse and releases its lock.
  • If necessary, consider other classes in the System.Threading namespace, such as WaitHandle, Auto/ManualResetEvent, Mutex, and Semaphore. Rarely consider the Interlocked methods, ReaderWriterLock, Thread.Interrupt, and asynchronous delegates.
  • Lock all writable shared state over the entire invariant, using private lock objects (not the instance or type object).
  • Avoid calling “third-party” code while you hold a lock, including overridable methods.
  • Use asynchronous I/O, e.g. Stream.BeginRead in .NET.
9/29/2005 9:58:22 AM (Pacific Daylight Time, UTC-07:00) | Comments [0] | Development#
Search
Archive
Links
Categories
Administration
Blogroll