2015-11-03 62 views
-1

我一直在努力通过来自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

的问题是在这里:

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

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

从MSDN网站上面链接:

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

+0

我明白了。谢谢。所以做这样的事情:'[ThreadStatic] private static Hashtable _localData; 私人静态Hashtable的localData { 得到 { 如果(_localData == NULL) \t { \t \t _localData =新的Hashtable(); \t} return _localData; } }' – brainbolt

+0

@brainbolt是的,这将工作。 –