0

出于示例目的,我已简化此代码:

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 );
       ....
    }
}

InvalidArgumentException在尝试更新 StreamWriter 时得到一个,因为m_FileStream已经被另一个线程处理并且为空(m_StreamWriter也是空的)。如何在成员变量周围加锁?

4

3 回答 3

2

你应该做这样的事情

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;
        }
    }
}

CalledFromOtherThread从另一个线程调用时,它应该获取锁,然后释放 m_FileStream。这样,CreateNewLogFile您将永远不会有处置的 FileStream

于 2012-08-03T22:20:01.987 回答
1

ThreadLocal更好_

 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();
    }
于 2012-08-03T22:23:11.353 回答
1

如果您的IDisposable对象实例已被其Dispose()调用的方法处理掉,则该对象引用是 - 或者应该是 - 不再可用,因为它的内部状态已被销毁等待垃圾回收。

您应该实例化一个新的对象实例,而不是尝试重用现有的对象实例。

当对已处置的对象执行任何操作时,精心设计的对象应抛出InvalidOperationException的特定子类型: ObjectDisposedException(可能的处置后调用例外Dispose()。)

IDisposable的文档在社区内容部分中有一个很好的IDisposable线程安全实现的示例。

于 2012-08-03T23:22:12.553 回答