Reputation: 11287
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
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
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
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