如何锁定成员变量?

时间:2012-08-03 22:12:01

标签: c# multithreading locking

我已将此代码简化为示例目的:

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

我在尝试新建StreamWriter时得到InvalidArgumentException,因为m_FileStream已被另一个线程处理并且为空(m_StreamWriter也为空)。如何锁定成员变量?

3 个答案:

答案 0 :(得分: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

答案 1 :(得分: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();
    }

答案 2 :(得分:1)

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

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

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

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