3

I have an object that is not safe for multithreaded applications (in several ways), and I want to provide an internal check to make sure that critical methods are not accessed concurrently.

Question

What techniques should I employ to detect and prevent multiple threads from accessing my class?

Is it sufficient to track the Thread.ID on all methods, properties, etc. that the consumer may use?

makerofthings7
  • 60,103
  • 53
  • 215
  • 448
  • 1
    the work you would do to accomplish that would be better spent making the class thread safe. Which is to say, Brian's answer is the right one: Just document that it's not thread safe. – Andrew Barber May 15 '12 at 19:52
  • 1
    I would like to know more about what conditions lead to thread safety concerns. Are there static methods that could interract unpredictably? Do you want to prevent two different threads from creating two instances? – EtherDragon May 15 '12 at 19:56
  • @EtherDragon - exactly. There are several degrees of thread-safetyness. – Martin James May 15 '12 at 23:29
  • @EtherDragon I wish I knew of all the examples, but one that comes to mind is "not safe for enumeration when items are added or removed". This most often occurs for me with a `Linkedlist`, or a Dictionary forcing me to copy the entire array. Question: Is there a list of the multithreaded scenarios that may come into play (checklist style)? – makerofthings7 May 22 '12 at 03:09
  • @MartinJames I replied to the comment from EtherDragon... I assumed you +1'd him – makerofthings7 May 22 '12 at 03:10
  • I guess that you are asking for something similar to the CrossThread call validation the current .Net framework versions are doing when you accidentally access an UI element from a sencondary thread, right? I also wonder how that is implemented... To me it seems that your idea of asserting the uniqueness of the calling Thread.ID (stored in a static member) will work, although I can't think of any clever ways of implementing other than checking on every method or property accessor of the class. This might be difficult to enforce on derived classes if your class is not sealed. Good Luck! –  Aug 02 '12 at 00:10
  • @sgorozco CrossThread is new to me but conceptually it seems to relate. Perhaps some IL magic could apply that check to every method and setter. – makerofthings7 Aug 02 '12 at 13:36

5 Answers5

8

Just document that it isn't thread safe. That's the convention used by the classes in .NET.

Brian Rasmussen
  • 114,645
  • 34
  • 221
  • 317
  • The consumer can ensure that it's accessed properly if they need to use the object across several threads. – Servy May 15 '12 at 19:50
  • It would be nice to be told just how thread-unsafe the class is. A class may work OK if a separate instance is created by each thread or if only one thread at a time is allowed to make method calls. – Martin James May 15 '12 at 23:26
  • @MartinJames: Sorry, but I'm a little confused by your comment. If you're not accessing state from more than one thread it doesn't really matter if the type is thread safe or not. There's no problem in that case. Only allowing one thread at a time would be synchronization and that's certainly one way to make operations thread safe. – Brian Rasmussen May 15 '12 at 23:36
2

Will it help?

lock(_padLock)
{
   ....
}

or if you have your object accessed externally, you could use Mutex object.

Val Bakhtin
  • 1,434
  • 9
  • 11
1

The best internal check you can possibly use is the lock setup that others have suggested. But here are some creative ways worth investigating to see if they solve your specific problem. The issue I have is, I don't know if only parts of the class is unsafe, if it's thread-safe to have different threads using different instances of the class or not. So here are some combinations of lock that mitigate certain scenarios

Scenario 1: Static Class/Methods - This will ensure that no matter who accesses the static class/members they will be blocked if another thread is already doing so.

public static class UnsafeStaticClass
{
    private static object _padLock = new object();

    public static void UnsafeStaticMethod()
    {
        lock(_padLock)
        {
            // Do Work
        }
    }
}

Scenario 2: Normal class where instances can be paralel, but only one call per instance is allowed. Multiple instances can run the code block at the same time.

public class UnsafeClass
{
    private object _padLock = new object();

    public void UnsafeMethod()
    {
        lock(_padLock)
        {
            // Do Work
        }
    }
}

Scenario 3: A Class instance, that interracts with unsafe static methods - hence a need to ensure that only a single call to ouside static methods exists. No matter how many instances there are, only one of them can run the method at a time, since it locks on a static object.

public void UnsafeClass
{
    private static object _padLock = new object();

    public void UnsafeInstanceMethod()
    {
        lock(_padlock)
        {
            // Do Work
        }
    }
}        
EtherDragon
  • 2,679
  • 1
  • 18
  • 24
0

Checking the thread ID's will catch multi-threaded use, in that if someone uses it in a multi-threaded program it will toss out messages that will, hopefully, stop people from using the program. It won't be elegant, but it will alert people to trouble eventually. How the program is used will determine the likelyhood of serious harm being done before people realize their error.

But in general, single-threaded code works really well in multi-threaded programs. It's fast and (relatively) easy to write. (Of course, the calling code has to be very careful.) You would be giving up this capability for your class and limiting it to run only in programs that will take care to run it in one single thread, from the start to the end of the program. In a windows desktop program, for instance, this pretty much means it has to run in the event queue, which means it will necessarily tie up the event queue while it runs--no worker threads!

Most collection classes are single-threaded, but get extensive use in multi-threaded programs. I'd use them as a model for single-threaded coding.

RalphChapin
  • 3,108
  • 16
  • 18
0

The attribute MethodImplOptions has an enum for Synchronized

Specifies that the method can be executed by only one thread at a time. Static methods lock on the type, whereas instance methods lock on the instance. Only one thread can execute in any of the instance functions, and only one thread can execute in any of a class's static functions.

...though this is not recommended. This method does the functional equivalent of lock(this) which allows the lock to be taken accidentally or maliciously by other classes

Another option is to use a separate object is used for locking. When doing this it can be marked as readonly, unless it's a struct like Spinlock. In short: don't use the readonly attribute for Spinlock : this cause every call to SpinLock or _lock.Enter to get a new copy of the object and succeed. Corruption is likely if this is done.

  class myClass
  {
      private object myLock;           //OK
      private readonly object myLock2; //OK
      private readonly SpinLock myLock3;  //WARNING: Value type, won't work as intended
      private SpinLock myLock4;  //OK

     void DoStuff()
     {
        lock(myLock4)
        {
           // some quick instructions... say 10 or fewer
        }
     }
  }
makerofthings7
  • 60,103
  • 53
  • 215
  • 448