在C#中,用於存儲的結構較多,如:DataTable,DataSet,List,Dictionary,Stack等結構,各種結構採用的存儲的方式存在差異,效率也必然各有優缺點。現在介紹一種後進先出的數據結構。 談到存儲結構,我們在項目中使用的較多。對於Task存儲結構,棧與隊列是類似的結構,在使用的 ...
在C#中,用於存儲的結構較多,如:DataTable,DataSet,List,Dictionary,Stack等結構,各種結構採用的存儲的方式存在差異,效率也必然各有優缺點。現在介紹一種後進先出的數據結構。
談到存儲結構,我們在項目中使用的較多。對於Task存儲結構,棧與隊列是類似的結構,在使用的時候採用不同的方法。C#中棧(Stack)是編譯期間就分配好的記憶體空間,因此你的代碼中必須就棧的大小有明確的定義;堆是程式運行期間動態分配的記憶體空間,你可以根據程式的運行情況確定要分配的堆記憶體的大小。
在C#中,棧通常保存著我們代碼執行的步驟。C#中的引用類型存儲在棧中,在程式運行的時候,每個線程(Thread)都會維護一個自己的專屬線程堆棧。當一個方法被調用的時候,主線程開始在所屬程式集的元數據中,查找被調用方法,然後通過JIT即時編譯並把結果(一般是本地CPU指令)放在棧頂。CPU通過匯流排從棧頂取指令,驅動程式以執行下去。
以上對棧這個數據結構進行了一個簡單的介紹,現在看一下C#實現棧結構的底層方法:
/// <summary> /// 初始化 <see cref="T:System.Collections.Generic.Stack`1"/> 類的新實例,該實例為空並且具有預設初始容量。 /// </summary> [__DynamicallyInvokable] public Stack(); /// <summary> /// 初始化 <see cref="T:System.Collections.Generic.Stack`1"/> 類的新實例,該實例為空,具有指定的初始容量或預設的初始容量(其中較大的一個)。 /// </summary> /// <param name="capacity"><see cref="T:System.Collections.Generic.Stack`1"/> 可包含的初始元素數。</param><exception cref="T:System.ArgumentOutOfRangeException"><paramref name="capacity"/> is less than zero.</exception> [__DynamicallyInvokable] public Stack(int capacity); /// <summary> /// 初始化 <see cref="T:System.Collections.Generic.Stack`1"/> 類的新實例,該實例包含從指定集合複製的元素並且具有足夠的容量來容納所複製的元素。 /// </summary> /// <param name="collection">從中複製元素的集合。</param><exception cref="T:System.ArgumentNullException"><paramref name="collection"/> is null.</exception> [__DynamicallyInvokable] public Stack(IEnumerable<T> collection);
以上是對stack的部分方法的介紹,由於在操作數據存儲的同時,會考慮到線程的安全性。
進程作為操作系統執行程式的基本單位,擁有應用程式的資源,進程包含線程,進程的資源被線程共用,線程不擁有資源。線程分為前臺線程和後臺線程,通過Thread類新建線程預設為前臺線程。當所有前臺線程關閉時,所有的後臺線程也會被直接終止,不會拋出異常。
接下來看一下ReaderWriterLockSlim類:
/// <summary> /// 表示用於管理資源訪問的鎖定狀態,可實現多線程讀取或進行獨占式寫入訪問。 /// </summary> [__DynamicallyInvokable] [HostProtection(SecurityAction.LinkDemand, ExternalThreading = true, Synchronization = true)] [HostProtection(SecurityAction.LinkDemand, MayLeakOnAbort = true)] public class ReaderWriterLockSlim : IDisposable { /// <summary> /// 使用預設屬性值初始化 <see cref="T:System.Threading.ReaderWriterLockSlim"/> 類的新實例。 /// </summary> [__DynamicallyInvokable] public ReaderWriterLockSlim(); /// <summary> /// 在指定鎖定遞歸策略的情況下初始化 <see cref="T:System.Threading.ReaderWriterLockSlim"/> 類的新實例。 /// </summary> /// <param name="recursionPolicy">枚舉值之一,用於指定鎖定遞歸策略。</param> [__DynamicallyInvokable] public ReaderWriterLockSlim(LockRecursionPolicy recursionPolicy); /// <summary> /// 嘗試進入讀取模式鎖定狀態。 /// </summary> /// <exception cref="T:System.Threading.LockRecursionException"><see cref="P:System.Threading.ReaderWriterLockSlim.RecursionPolicy"/> 屬性是 <see cref="F:System.Threading.LockRecursionPolicy.NoRecursion"/> 和當前的線程已進入讀取的模式。- 或 -當它已經包含寫入鎖時,當前線程可能不會獲取讀的鎖定。- 或 -遞歸數將超出該計數器的容量。此限制是很大的應用程式應永遠不會遇到它。</exception><exception cref="T:System.ObjectDisposedException"><see cref="T:System.Threading.ReaderWriterLockSlim"/> 對象已被釋放。</exception> [__DynamicallyInvokable] public void EnterReadLock(); /// <summary> /// 嘗試進入讀取模式鎖定狀態,可以選擇超時時間。 /// </summary> /// /// <returns> /// 如果調用線程已進入讀取模式,則為 true;否則為 false。 /// </returns> /// <param name="timeout">等待的間隔;或為 -1 毫秒,表示無限期等待。</param><exception cref="T:System.Threading.LockRecursionException"><see cref="P:System.Threading.ReaderWriterLockSlim.RecursionPolicy"/> 屬性是 <see cref="F:System.Threading.LockRecursionPolicy.NoRecursion"/> 和當前的線程已進入該鎖。- 或 -遞歸數將超出該計數器的容量。限製為應用程式應永遠不會遇到它太大。</exception><exception cref="T:System.ArgumentOutOfRangeException">值 <paramref name="timeout"/> 為負數,但它不等於-1 毫秒為單位),這是唯一允許的值為負。- 或 -值 <paramref name="timeout"/> 大於 <see cref="F:System.Int32.MaxValue"/> 毫秒為單位)。</exception><exception cref="T:System.ObjectDisposedException"><see cref="T:System.Threading.ReaderWriterLockSlim"/> 對象已被釋放。</exception> [__DynamicallyInvokable] public bool TryEnterReadLock(TimeSpan timeout); /// <summary> /// 嘗試進入讀取模式鎖定狀態,可以選擇整數超時時間。 /// </summary> /// /// <returns> /// 如果調用線程已進入讀取模式,則為 true;否則為 false。 /// </returns> /// <param name="millisecondsTimeout">等待的毫秒數,或為 -1 (<see cref="F:System.Threading.Timeout.Infinite"/>),表示無限期等待。</param><exception cref="T:System.Threading.LockRecursionException"><see cref="P:System.Threading.ReaderWriterLockSlim.RecursionPolicy"/> 屬性是 <see cref="F:System.Threading.LockRecursionPolicy.NoRecursion"/> 和當前的線程已進入該鎖。- 或 -遞歸數將超出該計數器的容量。限製為應用程式應永遠不會遇到它太大。</exception><exception cref="T:System.ArgumentOutOfRangeException">值 <paramref name="millisecondsTimeout"/> 為負數,但它不是等於 <see cref="F:System.Threading.Timeout.Infinite"/> (-1),這是唯一允許的值為負。</exception><exception cref="T:System.ObjectDisposedException"><see cref="T:System.Threading.ReaderWriterLockSlim"/> 對象已被釋放。</exception> [__DynamicallyInvokable] public bool TryEnterReadLock(int millisecondsTimeout); /// <summary> /// 嘗試進入寫入模式鎖定狀態。 /// </summary> /// <exception cref="T:System.Threading.LockRecursionException"><see cref="P:System.Threading.ReaderWriterLockSlim.RecursionPolicy"/> 屬性是 <see cref="F:System.Threading.LockRecursionPolicy.NoRecursion"/> 和當前的線程已在任何模式下進入該鎖。- 或 -當前線程已進入讀取的模式,因此嘗試進入鎖定狀態寫模式,則會創建導致死鎖的可能性。- 或 -遞歸數將超出該計數器的容量。限製為應用程式應永遠不會遇到它太大。</exception><exception cref="T:System.ObjectDisposedException"><see cref="T:System.Threading.ReaderWriterLockSlim"/> 對象已被釋放。</exception> [__DynamicallyInvokable] public void EnterWriteLock(); /// <summary> /// 嘗試進入寫入模式鎖定狀態,可以選擇超時時間。 /// </summary> /// /// <returns> /// 如果調用線程已進入寫入模式,則為 true;否則為 false。 /// </returns> /// <param name="timeout">等待的間隔;或為 -1 毫秒,表示無限期等待。</param><exception cref="T:System.Threading.LockRecursionException"><see cref="P:System.Threading.ReaderWriterLockSlim.RecursionPolicy"/> 屬性是 <see cref="F:System.Threading.LockRecursionPolicy.NoRecursion"/> 和當前的線程已進入該鎖。- 或 -當前線程最初在讀取模式中,輸入該鎖,因此嘗試進入寫入模式會創建導致死鎖的可能性。- 或 -遞歸數將超出該計數器的容量。限製為應用程式應永遠不會遇到它太大。</exception><exception cref="T:System.ArgumentOutOfRangeException">值 <paramref name="timeout"/> 為負數,但它不等於-1 毫秒為單位),這是唯一允許的值為負。- 或 -值 <paramref name="timeout"/> 大於 <see cref="F:System.Int32.MaxValue"/> 毫秒為單位)。</exception><exception cref="T:System.ObjectDisposedException"><see cref="T:System.Threading.ReaderWriterLockSlim"/> 對象已被釋放。</exception> [__DynamicallyInvokable] public bool TryEnterWriteLock(TimeSpan timeout); /// <summary> /// 嘗試進入寫入模式鎖定狀態,可以選擇超時時間。 /// </summary> /// /// <returns> /// 如果調用線程已進入寫入模式,則為 true;否則為 false。 /// </returns> /// <param name="millisecondsTimeout">等待的毫秒數,或為 -1 (<see cref="F:System.Threading.Timeout.Infinite"/>),表示無限期等待。</param><exception cref="T:System.Threading.LockRecursionException"><see cref="P:System.Threading.ReaderWriterLockSlim.RecursionPolicy"/> 屬性是 <see cref="F:System.Threading.LockRecursionPolicy.NoRecursion"/> 和當前的線程已進入該鎖。- 或 -當前線程最初在讀取模式中,輸入該鎖,因此嘗試進入寫入模式會創建導致死鎖的可能性。- 或 -遞歸數將超出該計數器的容量。限製為應用程式應永遠不會遇到它太大。</exception><exception cref="T:System.ArgumentOutOfRangeException">值 <paramref name="millisecondsTimeout"/> 為負數,但它不是等於 <see cref="F:System.Threading.Timeout.Infinite"/> (-1),這是唯一允許的值為負。</exception><exception cref="T:System.ObjectDisposedException"><see cref="T:System.Threading.ReaderWriterLockSlim"/> 對象已被釋放。</exception> [__DynamicallyInvokable] public bool TryEnterWriteLock(int millisecondsTimeout); /// <summary> /// 嘗試進入可升級模式鎖定狀態。 /// </summary> /// <exception cref="T:System.Threading.LockRecursionException"><see cref="P:System.Threading.ReaderWriterLockSlim.RecursionPolicy"/> 屬性是 <see cref="F:System.Threading.LockRecursionPolicy.NoRecursion"/> 和當前的線程已在任何模式下進入該鎖。- 或 -當前線程已進入讀取的模式,因此嘗試進入可升級模式將有死鎖的可能性。- 或 -遞歸數將超出該計數器的容量。限製為應用程式應永遠不會遇到它太大。</exception><exception cref="T:System.ObjectDisposedException"><see cref="T:System.Threading.ReaderWriterLockSlim"/> 對象已被釋放。</exception> [__DynamicallyInvokable] public void EnterUpgradeableReadLock(); /// <summary> /// 嘗試進入可升級模式鎖定狀態,可以選擇超時時間。 /// </summary> /// /// <returns> /// 如果調用線程已進入可升級模式,則為 true;否則為 false。 /// </returns> /// <param name="timeout">等待的間隔;或為 -1 毫秒,表示無限期等待。</param><exception cref="T:System.Threading.LockRecursionException"><see cref="P:System.Threading.ReaderWriterLockSlim.RecursionPolicy"/> 屬性是 <see cref="F:System.Threading.LockRecursionPolicy.NoRecursion"/> 和當前的線程已進入該鎖。- 或 -當前線程最初在讀取模式中,輸入該鎖,因此嘗試進入可升級模式會創建導致死鎖的可能性。- 或 -遞歸數將超出該計數器的容量。限製為應用程式應永遠不會遇到它太大。</exception><exception cref="T:System.ArgumentOutOfRangeException">值 <paramref name="timeout"/> 為負數,但它不等於-1 毫秒為單位),這是唯一允許的值為負。- 或 -值 <paramref name="timeout"/> 大於 <see cref="F:System.Int32.MaxValue"/> 毫秒為單位)。</exception><exception cref="T:System.ObjectDisposedException"><see cref="T:System.Threading.ReaderWriterLockSlim"/> 對象已被釋放。</exception> [__DynamicallyInvokable] public bool TryEnterUpgradeableReadLock(TimeSpan timeout); /// <summary> /// 嘗試進入可升級模式鎖定狀態,可以選擇超時時間。 /// </summary> /// /// <returns> /// 如果調用線程已進入可升級模式,則為 true;否則為 false。 /// </returns> /// <param name="millisecondsTimeout">等待的毫秒數,或為 -1 (<see cref="F:System.Threading.Timeout.Infinite"/>),表示無限期等待。</param><exception cref="T:System.Threading.LockRecursionException"><see cref="P:System.Threading.ReaderWriterLockSlim.RecursionPolicy"/> 屬性是 <see cref="F:System.Threading.LockRecursionPolicy.NoRecursion"/> 和當前的線程已進入該鎖。- 或 -當前線程最初在讀取模式中,輸入該鎖,因此嘗試進入可升級模式會創建導致死鎖的可能性。- 或 -遞歸數將超出該計數器的容量。限製為應用程式應永遠不會遇到它太大。</exception><exception cref="T:System.ArgumentOutOfRangeException">值 <paramref name="millisecondsTimeout"/> 為負數,但它不是等於 <see cref="F:System.Threading.Timeout.Infinite"/> (-1),這是唯一允許的值為負。</exception><exception cref="T:System.ObjectDisposedException"><see cref="T:System.Threading.ReaderWriterLockSlim"/> 對象已被釋放。</exception> [__DynamicallyInvokable] public bool TryEnterUpgradeableReadLock(int millisecondsTimeout); /// <summary> /// 減少讀取模式的遞歸計數,併在生成的計數為 0(零)時退出讀取模式。 /// </summary> /// <exception cref="T:System.Threading.SynchronizationLockException">在讀取模式中,當前線程不已進入該鎖。</exception> [__DynamicallyInvokable] public void ExitReadLock(); /// <summary> /// 減少寫入模式的遞歸計數,併在生成的計數為 0(零)時退出寫入模式。 /// </summary> /// <exception cref="T:System.Threading.SynchronizationLockException">當前線程不已進入寫入模式的鎖定。</exception> [__DynamicallyInvokable] public void ExitWriteLock(); /// <summary> /// 減少可升級模式的遞歸計數,併在生成的計數為 0(零)時退出可升級模式。 /// </summary> /// <exception cref="T:System.Threading.SynchronizationLockException">當前線程不已進入可升級模式的鎖定。</exception> [__DynamicallyInvokable] public void ExitUpgradeableReadLock(); /// <summary> /// 釋放 <see cref="T:System.Threading.ReaderWriterLockSlim"/> 類的當前實例所使用的所有資源。 /// </summary> /// <exception cref="T:System.Threading.SynchronizationLockException"><see cref="P:System.Threading.ReaderWriterLockSlim.WaitingReadCount"/> 是大於零。- 或 -<see cref="P:System.Threading.ReaderWriterLockSlim.WaitingUpgradeCount"/> 是大於零。- 或 -<see cref="P:System.Threading.ReaderWriterLockSlim.WaitingWriteCount"/> 是大於零。</exception><filterpriority>2</filterpriority> [__DynamicallyInvokable] public void Dispose(); /// <summary> /// 獲取一個值,該值指示當前線程是否已進入讀取模式的鎖定狀態。 /// </summary> /// /// <returns> /// 如果當前線程已進入讀取模式,則為 true;否則為 false。 /// </returns> /// <filterpriority>2</filterpriority> [__DynamicallyInvokable] public bool IsReadLockHeld { [__DynamicallyInvokable] get; } /// <summary> /// 獲取一個值,該值指示當前線程是否已進入可升級模式的鎖定狀態。 /// </summary> /// /// <returns> /// 如果當前線程已進入可升級模式,則為 true;否則為 false。 /// </returns> /// <filterpriority>2</filterpriority> [__DynamicallyInvokable] public bool IsUpgradeableReadLockHeld { [__DynamicallyInvokable] get; } /// <summary> /// 獲取一個值,該值指示當前線程是否已進入寫入模式的鎖定狀態。 /// </summary> /// /// <returns> /// 如果當前線程已進入寫入模式,則為 true;否則為 false。 /// </returns> /// <filterpriority>2</filterpriority> [__DynamicallyInvokable] public bool IsWriteLockHeld { [__DynamicallyInvokable] get; } /// <summary> /// 獲取一個值,該值指示當前 <see cref="T:System.Threading.ReaderWriterLockSlim"/> 對象的遞歸策略。 /// </summary> /// /// <returns> /// 枚舉值之一,用於指定鎖定遞歸策略。 /// </returns> [__DynamicallyInvokable] public LockRecursionPolicy RecursionPolicy { [__DynamicallyInvokable] get; } /// <summary> /// 獲取已進入讀取模式鎖定狀態的獨有線程的總數。 /// </summary> /// /// <returns> /// 已進入讀取模式鎖定狀態的獨有線程的數量。 /// </returns> [__DynamicallyInvokable] public int CurrentReadCount { [__DynamicallyInvokable] get; } /// <summary> /// 獲取當前線程進入讀取模式鎖定狀態的次數,用於指示遞歸。 /// </summary> /// /// <returns> /// 如果當前線程未進入讀取模式,則為 0(零);如果線程已進入讀取模式但卻不是以遞歸方式進入的,則為 1;或者如果線程已經以遞歸方式進入鎖定模式 n - 1 次,則為 n。 /// </returns> /// <filterpriority>2</filterpriority> [__DynamicallyInvokable] public int RecursiveReadCount { [__DynamicallyInvokable] get; } /// <summary> /// 獲取當前線程進入可升級模式鎖定狀態的次數,用於指示遞歸。 /// </summary> /// /// <returns> /// 如果當前線程沒有進入可升級模式,則為 0;如果線程已進入可升級模式卻不是以遞歸方式進入的,則為 1;或者如果線程已經以遞歸方式進入可升級模式 n - 1 次,則為 n。 /// </returns> /// <filterpriority>2</filterpriority> [__DynamicallyInvokable] public int RecursiveUpgradeCount { [__DynamicallyInvokable] get; } /// <summary> /// 獲取當前線程進入寫入模式鎖定狀態的次數,用於指示遞歸。 /// </summary> /// /// <returns> /// 如果當前線程沒有進入寫入模式,則為 0;如果線程已進入寫入模式卻不是以遞歸方式進入的,則為 1;或者如果線程已經以遞歸方式進入寫入模式 n - 1 次,則為 n。 /// </returns> /// <filterpriority>2</filterpriority> [__DynamicallyInvokable] public int RecursiveWriteCount { [__DynamicallyInvokable] get; } /// <summary> /// 獲取等待進入讀取模式鎖定狀態的線程總數。 /// </summary> /// /// <returns> /// 等待進入讀取模式的線程總數。 /// </returns> /// <filterpriority>2</filterpriority> [__DynamicallyInvokable] public int WaitingReadCount { [__DynamicallyInvokable] get; } /// <summary> /// 獲取等待進入可升級模式鎖定狀態的線程總數。 /// </summary> /// /// <returns> /// 等待進入可升級模式的線程總數。 /// </returns> /// <filterpriority>2</filterpriority> [__DynamicallyInvokable] public int WaitingUpgradeCount { [__DynamicallyInvokable] get; } /// <summary> /// 獲取等待進入寫入模式鎖定狀態的線程總數。 /// </summary> /// /// <returns> /// 等待進入寫入模式的線程總數。 /// </returns> /// <filterpriority>2</filterpriority> [__DynamicallyInvokable] public int WaitingWriteCount { [__DynamicallyInvokable] get; } }
以上是對Stack和線程的相關知識的淺述,現在介紹一下線程安全的Stack:
/// <summary> /// 表示對象的後進先出線程安全集合(棧結構) /// </summary> /// <typeparam name="T"></typeparam> public class TStack<T> : IEnumerable<T>, ICollection { /// <summary> /// 內部堆棧 /// </summary> private readonly Stack<T> _mStack; /// <summary> /// 鎖訪問堆棧(用於管理資源訪問的鎖定狀態,可實現多線程讀取或進行獨占式寫入訪問。) /// </summary> private readonly ReaderWriterLockSlim _lockStack = new ReaderWriterLockSlim(); /// <summary> /// 僅用於SyncRoot屬性 /// </summary> private readonly object _objSyncRoot = new object(); // Variables /// <summary> /// 初始化一個新的實例 <see cref="TStack{T}"/> class. /// </summary> public TStack() { _mStack = new Stack<T>(); } /// <summary> /// 初始化一個新的實例 <see cref="TStack{T}"/> class. /// </summary> /// <param name="col"> /// 開始集合 /// </param> public TStack(IEnumerable<T> col) { _mStack = new Stack<T>(col); } // Init /// <summary> /// 獲取枚舉器 /// </summary> public IEnumerator<T> GetEnumerator() { Stack<T> localStack = null; // 初始化枚舉器 _lockStack.PerformUsingReadLock(() => { // 創建一個m_tlist副本 localStack = new Stack<T>(_mStack); }); // 獲取枚舉器 foreach (T item in localStack) yield return item; } /// <summary> /// 獲取枚舉器 /// </summary> IEnumerator IEnumerable.GetEnumerator() { Stack<T> localStack = null; // 初始化枚舉器 _lockStack.PerformUsingReadLock(() => { // 創建一個m_TList的副本 localStack = new Stack<T>(_mStack); }); // 獲取枚舉器 foreach (T item in localStack) yield return item; } /// <summary> /// 複製到一個數組 /// </summary> /// <param name="array"></param> /// <param name="index"></param> public void CopyTo(Array array, int index) { _lockStack.PerformUsingReadLock(() => _mStack.ToArray().CopyTo(array, index)); } /// <summary> ///堆棧中的項目數 /// </summary> public int Count { get { return _lockStack.PerformUsingReadLock(() => _mStack.Count); } } /// <summary> /// 總為真 /// </summary> public bool IsSynchronized { get { return true; } } /// <summary> ///同步根 /// </summary> public object SyncRoot { get { return _objSyncRoot; } } /// <summary> ///清除集合 /// </summary> public void Clear() { _lockStack.PerformUsingWriteLock(() => _mStack.Clear()); } // Clear /// <summary> ///如果項目在堆棧中,則為true /// </summary> /// <param name="item"></param> /// <returns></returns> public bool Contains(T item) { return _lockStack.PerformUsingReadLock(() => _mStack.Contains(item)); } // 包含 /// <summary> /// 返回堆棧中的頂部項,而不從堆棧中刪除它 /// </summary> /// <returns></returns> public T Peek() { return _lockStack.PerformUsingReadLock(() => _mStack.Peek()); } // Peek /// <summary> ///刪除並返回堆棧中的頂部項目 /// </summary> /// <returns></returns> public T Pop() { return _lockStack.PerformUsingWriteLock(() => _mStack.Pop()); } // Pop /// <summary> /// 將一個項目插入堆棧 /// </summary> /// <param name="item"></param> public void Push(T item) { _lockStack.PerformUsingWriteLock(() => _mStack.Push(item)); } // Push /// <summary> ///將堆棧轉換為數組 /// </summary> /// <returns></returns> public T[] ToArray() { return _lockStack.PerformUsingReadLock(() => _mStack.ToArray()); } // ToArray /// <summary> /// 將容量設置為堆棧中實際的元素數量 /// </summary> public void TrimExcess() { _lockStack.PerformUsingWriteLock(() => _mStack.TrimExcess()); } }
以上的操作方法繼承了IEnumerable<T>, ICollection兩個介面。有興趣的,可以對IEnumerable<T>, ICollection兩個介面進行細緻的瞭解。