Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Guarantee that a class can only be used by the thread that instantiated it

I've created a class that is not thread-safe and can lead to bad bugs if assumed to be thread-safe. While I work to make my class thread-safe, I'd like to make instances only usable by one thread. Currently my implementation is to check that the current thread is the same as the thread used to construct the instance at every exposure point.

public class NotThreadSafeClass
{
    private readonly int _creatorThreadId;

    public NotThreadSafeClass()
    {
        _creatorThreadId = Thread.CurrentThread.ManagedThreadId;
    }

    public string ExposedProp
    {
        get
        {
            AssertSameThread();
        return "My Prop";
        }
    }

    public void ExposedMethod()
    {
        AssertSameThread();
        /* Do stuff */
    }

    private void AssertSameThread()
    {
        Throw.If(_creatorThreadId != Thread.CurrentThread.ManagedThreadId,
                 @"NotThreadSafeClass is not thread safe.  Please don't use
           the same instance of NotThreadSafeClass in multiple threads.");
    }
}

Note: Throw.If is defined in http://www.codeducky.org/10-utilities-c-developers-should-know-part-one/

This pattern seems to work, but it's cumbersome and susceptible to bugs if a developer forgets to add this check to any new exposures. Is there a safer and/or more elegant way to ensure that an instance is only used by one thread?

like image 590
Steven Wexler Avatar asked Dec 31 '25 14:12

Steven Wexler


2 Answers

I think that short of using an AOP framework, you will have to "intercept" all such access to your class' methods/properties in your own code, just like you're describing.

I'm thinking Ninject's Interception Extension, or PostSharp

Nothing is built into the language/framework for this.

Cheers

like image 110
Luc Morin Avatar answered Jan 02 '26 03:01

Luc Morin


Edit: Moved ThreadLocal<T> to a private field inside the class declaration.

Unless I completely misunderstand, ThreadLocal<T> should meet your needs. An example:

class Foo {
  private ThreadLocal<int> _internalState;

  public Foo() {
    _internalState = new ThreadLocal<int>();
  }

  public int IntValue {
    get { return _internalState.Value; }
    set { _internalState.Value = value; }
  }

  public override string ToString() {
    return _internalState.ToString();
  }
}

class Program {
  public static void Main(string[] args) {
    Demonstrate();
  }

  static void Demonstrate() {
    var local = new Foo {IntValue = 5};
    Console.WriteLine("Start thread value: {0}", local.IntValue);

    new Thread(() => {
      local.IntValue += 5;
      Console.WriteLine("New thread value: {0}", local.IntValue);
    }).Start();

    local.IntValue += 10;
    Console.WriteLine("Start thread value: {0}", local.IntValue);
  }
}

Sample output:

Start thread value: 5
Start thread value: 15
New thread value: 5
like image 34
jdphenix Avatar answered Jan 02 '26 04:01

jdphenix