O.O
O.O

Reputation: 11287

How to lock a member variable?

I have simplified this code for example purposes:

class TextLogger : IDisposable
{
    private FileStream m_FileStream;
    private StreamWriter m_StreamWriter;

    void CreateNewLogFile()
    {
          //Open the File
       m_FileStream = File.Open(
           m_CurrentFileName,
           FileMode.OpenOrCreate,
           FileAccess.Write,
           FileShare.Read );

       m_StreamWriter = new StreamWriter( m_FileStream );
       ....
    }
}

I get an InvalidArgumentException when trying to new up the StreamWriter, because m_FileStream has already been disposed by another thread and is null (m_StreamWriter is also null). How do I put a lock around the member variable?

Upvotes: 1

Views: 989

Answers (3)

Nicholas Carey
Nicholas Carey

Reputation: 74257

If your IDisposable object instance has been disposed of by its Dispose() method being called, the object reference is — or should be — no longer usable as its internal state has been destroyed pending garbage collection.

You should instantiate a new object instance rather than trying to reuse the existing object instance.

A well-crafted object should throw a specific subtype of InvalidOperationException when any operation is performed on a disposed object: ObjectDisposedException (with the possible exception of post-disposal invocations of Dispose().)

The documentation for IDisposable has in the community content section a good example of a thread-safe implementation for IDisposable.

Upvotes: 1

GSerjo
GSerjo

Reputation: 4778

The ThreadLocal is better

 static void Main()
    {
        // Thread-Local variable that yields a name for a thread
        ThreadLocal<string> ThreadName = new ThreadLocal<string>(() =>
        {
            return "Thread" + Thread.CurrentThread.ManagedThreadId;
        });

        // Action that prints out ThreadName for the current thread
        Action action = () =>
        {
            // If ThreadName.IsValueCreated is true, it means that we are not the
            // first action to run on this thread.
            bool repeat = ThreadName.IsValueCreated;

            Console.WriteLine("ThreadName = {0} {1}", ThreadName.Value, repeat ? "(repeat)" : "");
        };

        // Launch eight of them.  On 4 cores or less, you should see some repeat ThreadNames
        Parallel.Invoke(action, action, action, action, action, action, action, action);

        // Dispose when you are done
        ThreadName.Dispose();
    }

Upvotes: 1

parapura rajkumar
parapura rajkumar

Reputation: 24403

You should do something like this

class TextLogger : IDisposable
{
    private FileStream m_FileStream;
    private StreamWriter m_StreamWriter;
    private object m_Lock = new object();

    void CreateNewLogFile()
    {
        lock (m_Lock)
        {
            if ( m_FileStream != null )
                m_StreamWriter = new StreamWriter(m_FileStream);
        };
    }

    void CalledFromOtherThread()
    {
        //Do stuff

        lock (m_Lock)
        {
            if (m_FileStream != null)
                m_FileStream.Dispose();
            m_FileStream = null;
        }
    }
}

When CalledFromOtherThread is called from another thread it should acquire the lock and then dispose the m_FileStream. This way in CreateNewLogFile you will never have a disposed FileStream

Upvotes: 3

Related Questions