多线程测试中的空引用

时间:2015-11-03 19:26:04

标签: c# multithreading nhibernate

我一直在使用来自NHibernate网站的this article来实现UnitOfWork模式,我遇到了一个我无法解决的问题。在第3节中,这是实现线程安全的地方,有一个测试是抛出空引用异常。我没有多线程经验,所以我不知道如何继续这里。

你能告诉我这是什么问题吗?

测试代码

private ManualResetEvent _event;

[Test]
public void Local_data_is_thread_local()
{
    Console.WriteLine("Starting in main thread {0}", Thread.CurrentThread.ManagedThreadId);
    Local.Data["one"] = "This is a string";
    Assert.AreEqual(1, Local.Data.Count);

    _event = new ManualResetEvent(false);
    var backgroundThread = new Thread(RunInOtherThread);
    backgroundThread.Start();

    // give the background thread some time to do its job
    Thread.Sleep(100); <<<<<<< ######## EXCEPTION AFTER THIS LINE #########
    // we still have only one entry (in this thread)
    Assert.AreEqual(1, Local.Data.Count);

    Console.WriteLine("Signaling background thread from main thread {0}", Thread.CurrentThread.ManagedThreadId);
    _event.Set();
    backgroundThread.Join();
}

private void RunInOtherThread()
{
    Console.WriteLine("Starting (background-) thread {0}", Thread.CurrentThread.ManagedThreadId);
    // initially the local data must be empty for this NEW thread!
    Assert.AreEqual(0, Local.Data.Count);
    Local.Data["one"] = "This is another string";
    Assert.AreEqual(1, Local.Data.Count);

    Console.WriteLine("Waiting on (background-) thread {0}", Thread.CurrentThread.ManagedThreadId);
    _event.WaitOne();
    Console.WriteLine("Ending (background-) thread {0}", Thread.CurrentThread.ManagedThreadId);
}

正在测试的代码

public interface ILocalData
{
    object this[object key] { get; set; }
    int Count { get; }
    void Clear();
}

public static class Local
{
    static readonly ILocalData _data = new LocalData();

    public static ILocalData Data
    {
        get { return _data; }
    }

    private class LocalData : ILocalData
    {
        [ThreadStatic]
        private static Hashtable _localData = new Hashtable();

        public object this[object key]
        {
            get { return _localData[key]; }
            set { _localData[key] = value; }
        }

        public int Count
        {
            get { return _localData.Count; } <<<<<<< ######## EXCEPTION HERE #########
        }

        public void Clear()
        {
            _localData.Clear();
        }
    }
}

从技术上讲,测试通过,但在输出中,我可以看到有一个空引用异常:

Run started: C:\Users\brainbolt\Documents\GitHub\NHibernateUnitOfWork\NHinbernateUnitOfWork.Testing\bin\Debug\NHinbernateUnitOfWork.Testing.dll
Starting in main thread 18
Starting (background-) thread 19
System.NullReferenceException: Object reference not set to an instance of an object.
   at NHibernateUnitOfWork.NHibernateUnitOfWork.Local.LocalData.get_Count() in c:\Users\brainbolt\Documents\GitHub\NHibernateUnitOfWork\NHibernateUnitOfWork\Local.cs:line 37
   at NHinbernateUnitOfWork.Testing.LocalData_Fixture.RunInOtherThread() in c:\Users\brainbolt\Documents\GitHub\NHibernateUnitOfWork\NHinbernateUnitOfWork.Testing\LocalData_Fixture.cs:line 73
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()
   at NHibernateUnitOfWork.NHibernateUnitOfWork.Local.LocalData.get_Count() in c:\Users\brainbolt\Documents\GitHub\NHibernateUnitOfWork\NHibernateUnitOfWork\Local.cs:line 37
   at NHinbernateUnitOfWork.Testing.LocalData_Fixture.RunInOtherThread() in c:\Users\brainbolt\Documents\GitHub\NHibernateUnitOfWork\NHinbernateUnitOfWork.Testing\LocalData_Fixture.cs:line 73
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()
Signaling background thread from main thread 18
NUnit VS Adapter 2.0.0.0 executing tests is finished

1 个答案:

答案 0 :(得分:1)

问题在于:

[ThreadStatic]
private static Hashtable _localData = new Hashtable();

ThreadStatic attribute表示_localData字段的值对于每个线程都是唯一的。但是,初始化将仅在类型构造函数中执行一次,因此只有执行类型构造函数的线程才会具有非null _localData。所有其他线程都有null

从上面链接的MSDN网站:

  

不要为标记有ThreadStaticAttribute的字段指定初始值,因为这样的初始化只在类构造函数执行时发生一次,因此只影响一个线程。如果未指定初始值,则可以依赖于初始化为默认值的字段(如果它是值类型),或者如果它是引用类型则为null。