2015-11-03 60 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是的,這將工作。 –