Technology Corner

Home » Multithreading » Multithreading Best Practices

Multithreading Best Practices

Enter your email address to follow this blog and receive notifications of new posts by email.

Join 80 other followers

Twitter updates

Archives

RSS InfoQ Feeds

  • Article: Q&A on the Book "Humans vs Computers"
    Author Gojko Adzic has released a book, Humans vs Computers in which he tells stories about the impact of inflexible automation, edge cases and software bugs on the lives of real people. He explains the common mistakes built into the systems and provides advice on how to prevent these mistakes from being built into our systems in the first place. By Shane Ha […]
  • Q&A with Michael Coté on Devops Adoption and his Talk at DevOpsDays NZ
    Raf Gemmail talks to Pivotal’s Michael Coté about obstacles to DevOps adoption and his forthcoming talk at DevOpsDays NZ 2017 By Rafiq Gemmail
  • TensorFlow Serving 1.0 Release Detailed at Google I/O
    Google's Noah Fiedel details new programming model for TensorFlow Serving in a stable 1.0 release. Subject matter addresses common challenges with portability, servablility , and reproducibility improvements. By Dylan Raithel
  • First NetBeans Code Drop Lands at Apache
    Oracle has released the first of three NetBeans code drops to the Apache Incubator. By Matt Raible
  • Article: The Top 10 Adages in Continuous Deployment
    On the basis of discussions at the Continuous Deployment Summit, researchers derived 10 adages about continuous-deployment practices. These adages represent a working set of approaches and beliefs that guide current practice and establish a tangible target for empirical validation. By Chris Parnin
  • Podcast: Joshua Kerievsky and Heidi Helfand on High Performance via Psychological Safety
    In this podcast Shane Hastie, Lead Editor for Culture & Methods, spoke to Joshua Kerievsky, CEO of Industrial Logic, and Heidi Helfand, Director of Engineering Excellence at Procore Technologies and author of the book Dynamic Reteaming, about their talk High Performance via Psychological Safety. By Joshua Kerievsky
  • Spotify and Google Release Forseti GCP Security Tools
    Google has opened up Forseti Security, a set open source tools for GCP security, to all GCP users. The project is the result of a collaborative effort from both Spotify and Google, combining what was originally separate work together into a single toolkit. It aims to automate security processes for developers in order for them to develop more freely. By Andr […]
  • Article: Q&A on the Book SAFe Distilled
    The book SAFe Distilled breaks down the complexity of the framework into easily understood explanations and actionable guidance. It’s a resource for acquiring a deep understanding of the Scaled Agile Framework, and how to implement it successfully. By Ben Linders
  • String Interpolation in Entity Framework Raises Concerns
    One of the new features in Entity Framework Core 2 is the ability to automatically convert interpolated strings into parameterized SQL. Though designed to avoid problems with poorly written SQL, it is feared that it may actually lead to more SQL injection attacks. By Jonathan Allen
  • Podcast: Twitter's Yao Yue on Latency, Performance Monitoring, & Caching at Scale
    Yao Yue spent the majority of her career working on caching systems at Twitter. She created a performance team that deals with edge performance outliers often exposed by the enormous scale of Twitter. In this podcast, she discusses standing up the performance team, thoughts on instrumenting applications, and interesting performance issues (and strategies for […]

Multithreading requires careful programming. For most tasks, you can reduce complexity by queuing requests for execution by thread pool threads. This topic addresses more difficult situations, such as coordinating the work of multiple threads, or handling threads that block.

Deadlocks and Race Conditions

Multithreading solves problems with throughput and responsiveness, but in doing so it introduces new problems: deadlocks and race conditions.

Deadlocks

A deadlock occurs when each of two threads tries to lock a resource the other has already locked. Neither thread can make any further progress.

Many methods of the managed threading classes provide time-outs to help you detect deadlocks. For example, the following code attempts to acquire a lock on the current instance. If the lock is not obtained in 300 milliseconds, Monitor.TryEnter returns false.

if (Monitor.TryEnter(this, 300)) {

try {

// Place code protected by the Monitor here.

}

finally {

Monitor.Exit(this);

}

}

else {

// Code to execute if the attempt times out.

}

Race Conditions

A race condition is a bug that occurs when the outcome of a program depends on which of two or more threads reaches a particular block of code first. Running the program many times produces different results, and the result of any given run cannot be predicted.

Race conditions can also occur when you synchronize the activities of multiple threads. Whenever you write a line of code, you must consider what might happen if a thread were preempted before executing the line (or before any of the individual machine instructions that make up the line), and another thread overtook it.

Number of Processors

  • Multithreading solves different problems for the single-processor computers that run most end-user software, and the multiprocessor computers typically used as servers.

Single-Processor Computers

  • Multithreading provides greater responsiveness to the computer user, and uses idle time for background tasks. If you use multithreading on a single-processor computer:
  • Only one thread runs at any instant.
  • A background thread executes only when the main user thread is idle. A foreground thread that executes constantly starves background threads of processor time.
  • When you call the Thread.Start method on a thread, that thread does not start executing until the current thread yields or is preempted by the operating system.
  • Race conditions typically occur because the programmer did not anticipate the fact that a thread can be preempted at an awkward moment, sometimes allowing another thread to reach a code block first.

Multiprocessor Computers

Multithreading provides greater throughput. Ten processors can do ten times the work of one, but only if the work is divided so that all ten can be working at once; threads provide an easy way to divide the work and exploit the extra processing power. If you use multithreading on a multiprocessor computer:

The number of threads that can execute concurrently is limited by the number of processors.

A background thread executes only when the number of foreground threads executing is smaller than the number of processors.

When you call the Thread.Start method on a thread, that thread might or might not start executing immediately, depending on the number of processors and the number of threads currently waiting to execute.

Race conditions can occur not only because threads are preempted unexpectedly, but because two threads executing on different processors might be racing to reach the same code block.

Static Members and Static Constructors

A class is not initialized until its class constructor (static constructor in C#, Shared Sub New in Visual Basic) has finished running. To prevent the execution of code on a type that is not initialized, the common language runtime blocks all calls from other threads to static members of the class (Shared members in Visual Basic) until the class constructor has finished running.

For example, if a class constructor starts a new thread, and the thread procedure calls a static member of the class, the new thread blocks until the class constructor completes.

This applies to any type that can have a static constructor.

General Recommendations

Consider the following guidelines when using multiple threads:

  • Don’t use Thread.Abort to terminate other threads. Calling Abort on another thread is akin to throwing an exception on that thread, without knowing what point that thread has reached in its processing.
  • Don’t use Thread.Suspend and Thread.Resume to synchronize the activities of multiple threads. Do use Mutex, ManualResetEvent, AutoResetEvent, and Monitor.
  • Don’t control the execution of worker threads from your main program (using events, for example). Instead, design your program so that worker threads are responsible for waiting until work is available, executing it, and notifying other parts of your program when finished. If your worker threads do not block, consider using thread pool threads. Monitor.PulseAll is useful in situations where worker threads block.
  • Don’t use types as lock objects. That is, avoid code such as lock(typeof(X)) in C#, or the use of Monitor.Enter with Type objects. For a given type, there is only one instance of System.Type per application domain. If the type you take a lock on is public, code other than your own can take locks on it, leading to deadlocks.
  • Use caution when locking on instances, for example lock(this). If other code in your application, external to the type, takes a lock on the object, deadlocks could occur.
  • Do ensure that a thread that has entered a monitor always leaves that monitor, even if an exception occurs while the thread is in the monitor. The C# lock statement provide this behavior automatically, employing a finally block to ensure that Monitor.Exit is called. If you cannot ensure that Exit will be called, consider changing your design to use Mutex. A mutex is automatically released when the thread that currently owns it terminates.
  • Do use multiple threads for tasks that require different resources, and avoid assigning multiple threads to a single resource. For example, any task involving I/O benefits from having its own thread, because that thread will block during I/O operations and thus allow other threads to execute. User input is another resource that benefits from a dedicated thread. On a single-processor computer, a task that involves intensive computation coexists with user input and with tasks that involve I/O, but multiple computation-intensive tasks contend with each other.
  • Consider using methods of the Interlocked class for simple state changes, instead of using the lock statement. The lock statement is a good general-purpose tool, but the Interlocked class provides better performance for updates that must be atomic. Internally, it executes a single lock prefix if there is no contention. In code reviews, watch for code like that shown in the following examples. In the first example, a state variable is incremented:
  lock(lockObject) 
    { 
	 myField++; 
    }

You can improve performance by using the Increment method instead of the lock statement, as follows:

System.Threading.Interlocked.Increment(myField);

In the second example, a reference type variable is updated only if it is a null reference (Nothing in Visual Basic).

   if (x == null)
            {
                lock (lockObject)
                {
                    if (x == null)
                    {
                        x = y;
                    }
                }
            }

Performance can be improved by using the CompareExchange method instead, as follows:

System.Threading.Interlocked.CompareExchange(ref x, y, null);
  • Use ReaderWriterLock object in case if there is more threads are reading data and less threads are writing data. using lock in this case may hamper performance of reading and writing.
  • Background Thread: When you are using background thread and application terminates (all foreground threads), all background threads will automatically terminate then any finally blocks in the execution stack of background threads will bypass. This is a problem if your program employs finally (or using) blocks to perform cleanup work such as releasing resources or deleting temporary files. To avoid this, you can explicitly wait out such background threads upon exiting an application. There are two ways to accomplish this:
    • If you’ve created the thread yourself, call Join on the thread.
    • If you’re on a pooled thread, use an event wait handle.

Recommendations for Class Libraries

Consider the following guidelines when designing class libraries for multithreading:

  • Avoid the need for synchronization, if possible. This is especially true for heavily used code. For example, an algorithm might be adjusted to tolerate a race condition rather than eliminate it. Unnecessary synchronization decreases performance and creates the possibility of deadlocks and race conditions.
  • Make static data (Shared in Visual Basic) thread safe by default.
  • Do not make instance data thread safe by default. Adding locks to create thread-safe code decreases performance, increases lock contention, and creates the possibility for deadlocks to occur. In common application models, only one thread at a time executes user code, which minimizes the need for thread safety. For this reason, the .NET Framework class libraries are not thread safe by default.
  • Avoid providing static methods that alter static state. In common server scenarios, static state is shared across requests, which means multiple threads can execute that code at the same time. This opens up the possibility of threading bugs. Consider using a design pattern that encapsulates data into instances that are not shared across requests. Furthermore, if static data are synchronized, calls between static methods that alter state can result in deadlocks or redundant synchronization, adversely affecting performance.
Advertisements

1 Comment

  1. himanshu2590 says:

    Reblogged this on Himanshu2590's Blog.

    Like

Leave a Reply

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

WordPress.com Logo

You are commenting using your WordPress.com 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 )

Google+ photo

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

Connecting to %s

Blogs I Follow

%d bloggers like this: