2

When creating a library for a simple program, is it more cost effective to make it thread safe or is there a way to detect the program's use in a multithreaded program and ASSERT() or otherwise determine (preferably at compile or link time) that it may create problems.

Related help for this question would be automated tool support for finding potential problems with thread safety, programming language features that enforce it,

2
  • 5
    Cost effective, simple, and thread safe. Hmmm. Quoting from Sesame Street, One of these things does not go with the others. One of these things doesn't belong. Sep 14, 2012 at 16:43
  • 1
    So I guess this is a vote for warning people using the library. Sep 14, 2012 at 16:50

4 Answers 4

1

It depends on the cost to make it thread safe (in terms of development time and effort). Things to consider include:

  • Making a library thread safe can be challenging, particularly when dealing with lower level thread primitives, such as Java's synchronized and C#'s lock keywords, when efficiency or speed is critical.
  • Some environments provide higher level abstractions like C# 4.5's await and async keywords. Use them if available.
  • Use thread-safe library classes, like those in .Net's System.Collections.Concurrent namespace.
  • Thread safety may impact the code's portability. For example, C code using pthread mutexes will not work on Windows without additional library support.

It also depends on how likely the library will be used in a multi-threaded environment and the conventions for the language used. For example, Microsoft original class library design guidelines stated that non-static members need not be thread safe.

Either way, the best approach is to clearly document what is and what is not thread safe.

13

So I guess this is a vote for warning people using the library.

Yes, that is exactly what my snarky comment was. Unless there is a good reason for adding that expense, don't do it. Thread safety is expensive, to the designer, to the developer, to the tester, and also to the user who doesn't need it. You are guilty of premature optimization unless there is a very good reason to absorb that expense.

  • Good reason: You are a developer working on a logging library where a single logged item might comprise many lines in the log file, and the logger might be called from multiple threads. The need for thread safety is right there in the requirements, and those requirements make sense.
  • Bad reason: After doing so well on the logging library, you are made the project lead that amongst other things will develop a random number library. Here the driving requirements are numerical correctness, ease of use, and speed. You, the project lead, decide to add thread safety to the requirements just because ensuring thread safety was so much fun in the logging library project. You've just blown away my need for speed. I might make trillions of calls to the PRNG during an overnight Monte Carlo run. Your library is about as useful to me as is the built-in rand() function.

You build in thread safety because it makes sense and because there is a need for it, and even then you need to be judicious about how you proceed.

2

Something like this (C#) MIGHT do the trick...

public class MyNonThreadSafeClass()
{
   public MyNonThreadSafeClass()
   {
       currentThreadId = Thread.CurrentThread.ManagedThreadId;
   }

   private readonly int currentThreadId;

   public void SomeNonThreadSafeMethod()
   {
      AssertSameThread();

      //do your thing   
   }

   private void AssertSameThread()
   {
      if(currentThreadId != Thread.CurrentThread.ManagedThreadId)
         throw new InvalidOperationException("Method must be called on the same thread that created the containing object");
   }
}

This would ensure that only the thread that created the object instance could work with it. Variations of this could ensure that no one method is called concurrently by different threads at the same time (but allow one thread to create the instance and then give it to another thread to work with), or that the creating thread must have a message pump (and is thus a UI thread and so very likely to be the app's "main" thread).

However, overall, if you expect your library to be used by parallel threads of a process, and want to support that, I would make it thread-safe. It's not really that hard; use monitors, mutexes or other locks to protect shared state, and favor "pure functions" that don't rely on instance-scoped state.

1
  • This is exactly what Windows GUI libraries do (e.g. gdi32.dll, user32.dll, and System.Windows.Forms.dll), and I think it's a very good answer to the actual question here. The answer that got the green check mark seems to be focused on strategies for making a library thread-safe. That's not what was asked, nor is it always possible, nor is it always advisable when it is possible. Nov 25, 2014 at 19:29
0

The biggest thing I would suggest is that a class should consider void any contracts with callers that use it improperly, but improper usage by one caller should not affect the validity of contracts with other callers.

Suppose, for example, that a class implements "Popsicle Immunity" and promises that if IsFrozen returns true, all subsequent observations of any particular property will yield the same result. Suppose further that one thread calls an instance's "Freeze" method while another thread is modifying that instance. The threads modifying the object or calling Freeze have no legitimate expectations about what the object might do, but code elsewhere which calls IsFrozen, sees it return true, and observes some other property of the object would be entitled to expect that latter property would never change; the fact that the former methods abused the object should not weaken the latter code's right to expect that an object which claims to be immutable, is.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.