c# 線程安全隊列的用法原理及使用示例
什么是線程安全?
答:線程安全是多線程編程時的計算機程序代碼中的一個概念。在擁有共享數(shù)據(jù)的多條線程并行執(zhí)行的程序中,線程安全的代碼會通過同步機制保證各個線程都可以正常且正確的執(zhí)行,不會出現(xiàn)數(shù)據(jù)污染等意外情況。
在多線程并行的情況下會出現(xiàn)共享數(shù)據(jù)會線程間讀取與寫入不一直的情況,為了解決這種情況,通常會使用鎖來解決,也就是將并行改為串行。但是在使用穿行違背了使用多線程并發(fā)的初衷,這種情況下我們可以考慮采用線程安全結(jié)構(gòu)。
先看下線程安全隊列的用法:
ConcurrentQueue<int> ts = new System.Collections.Concurrent.ConcurrentQueue<int>(); ts.Enqueue(1); ts.Enqueue(2); ts.Enqueue(3); ts.Enqueue(4); foreach (var r in ts) { Console.Write($"data:{r} "); } Console.WriteLine(); ts.TryPeek(out int pk); Console.WriteLine($"peek:{pk}"); ts.TryDequeue(out int ck); ts.Enqueue(5); ts.Enqueue(6); Console.WriteLine(); foreach (var r in ts) { Console.Write($"data:{r} "); } Console.WriteLine(); Console.ReadLine();
現(xiàn)在我們看下線程安全隊列的實現(xiàn)方式:(參考自:.net framework 4.8),核心代碼全部做了注釋。
總的來說,(總結(jié)語放到前面,防止代碼篇幅太大,同志們沒有耐心翻到最底下~)
1、線程安全隊列通過SpinWait自旋類來實現(xiàn)等待并行線程完成與Interlocked原子操作類計數(shù)實現(xiàn)的。
2、線程安全隊列通過單向鏈表實現(xiàn)的,鏈的節(jié)點為長度32的數(shù)組,通過記錄鏈的頭節(jié)點與尾節(jié)點、以及隊列的頭尾實現(xiàn)隊列的存儲與入隊、出隊操作的。
public class MyConcurrentQueue<T> : IProducerConsumerCollection<T> { [NonSerialized] private volatile Segment m_head; [NonSerialized] private volatile Segment m_tail; private T[] m_serializationArray; private const int SEGMENT_SIZE = 32; [NonSerialized] internal volatile int m_numSnapshotTakers = 0; /// <summary> /// 鏈尾部節(jié)點 /// </summary> public MyConcurrentQueue() { m_head = m_tail = new Segment(0, this); } //嘗試添加 bool IProducerConsumerCollection<T>.TryAdd(T item) { Enqueue(item); return true; } /// <summary> /// 嘗試從中移除并返回對象 /// </summary> /// <param name="item"> /// </remarks> bool IProducerConsumerCollection<T>.TryTake(out T item) { return TryDequeue(out item); } /// <summary> /// 判斷當(dāng)前鏈?zhǔn)欠駷榭? /// </summary> public bool IsEmpty { get { Segment head = m_head; if (!head.IsEmpty) //如果頭不為空,則鏈非空 return false; else if (head.Next == null) //如果頭節(jié)點的下一個節(jié)點為空,且為鏈尾, return true; else //如果頭節(jié)點為空且不是最后一個節(jié)點 ,則標(biāo)識另一個線程正在寫入該數(shù)組 //等待中.. { SpinWait spin = new SpinWait(); while (head.IsEmpty) { //此時為空 if (head.Next == null) return true; //否則標(biāo)識正在有線程占用寫入 //線程循環(huán)一次 spin.SpinOnce(); head = m_head; } return false; } } } /// <summary> /// 用來判斷鏈?zhǔn)欠裨谧兓? /// </summary> /// <param name="head"></param> /// <param name="tail"></param> /// <param name="headLow"></param> /// <param name="tailHigh"></param> private void GetHeadTailPositions(out Segment head, out Segment tail, out int headLow, out int tailHigh) { head = m_head; tail = m_tail; headLow = head.Low; tailHigh = tail.High; SpinWait spin = new SpinWait(); Console.WriteLine($"head.Low:{head.Low},tail.High:{tail.High},head.m_index:{head.m_index},tail.m_index:{tail.m_index}"); //通過循環(huán)來保證值不再更改(也就是說并行線程操作結(jié)束) //保證線程串行核心的判斷邏輯 while ( //頭尾發(fā)生變化 head != m_head || tail != m_tail //如果隊列頭、尾索引發(fā)生變化 || headLow != head.Low || tailHigh != tail.High || head.m_index > tail.m_index) { spin.SpinOnce(); head = m_head; tail = m_tail; headLow = head.Low; tailHigh = tail.High; } } /// <summary> /// 獲取總數(shù) /// </summary> public int Count { get { Segment head, tail; int headLow, tailHigh; GetHeadTailPositions(out head, out tail, out headLow, out tailHigh); if (head == tail) { return tailHigh - headLow + 1; } //頭節(jié)點長度 int count = SEGMENT_SIZE - headLow; //加上中間其他節(jié)點長度 count += SEGMENT_SIZE * ((int)(tail.m_index - head.m_index - 1)); //加上尾節(jié)點長度 count += tailHigh + 1; return count; } } public object SyncRoot => throw new NotImplementedException(); public bool IsSynchronized => throw new NotImplementedException(); public void CopyTo(T[] array, int index) { } /// <summary> /// 暫未實現(xiàn) /// </summary> /// <returns></returns> public IEnumerator<T> GetEnumerator() { return null; } /// <summary> /// 添加 /// </summary> /// <param name="item"></param> public void Enqueue(T item) { SpinWait spin = new SpinWait(); while (true) { Segment tail = m_tail; if (tail.TryAppend(item)) return; spin.SpinOnce(); } } /// <summary> /// 嘗試刪除節(jié)點 /// </summary> /// <param name="result"></param> /// <returns></returns> public bool TryDequeue(out T result) { while (!IsEmpty) { Segment head = m_head; if (head.TryRemove(out result)) return true; } result = default(T); return false; } /// <summary> /// 查看最后一個添加入的元素 /// </summary> /// <param name="result"></param> /// <returns></returns> public bool TryPeek(out T result) { //原子增加值 Interlocked.Increment(ref m_numSnapshotTakers); while (!IsEmpty) { //首先從頭節(jié)點看一下第一個節(jié)點是否存在 Segment head = m_head; if (head.TryPeek(out result)) { Interlocked.Decrement(ref m_numSnapshotTakers); return true; } } result = default(T); Interlocked.Decrement(ref m_numSnapshotTakers); return false; } public void CopyTo(Array array, int index) { throw new NotImplementedException(); } IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); } public T[] ToArray() { throw new NotImplementedException(); } /// <summary> /// 為線程安全隊列提供一個 單向鏈表, /// 鏈表的每個節(jié)點存儲長度為32的數(shù)組 /// </summary> private class Segment { /// <summary> /// 定義一個數(shù)組,用于存儲每個節(jié)點的內(nèi)容 /// </summary> internal volatile T[] m_array; /// <summary> /// 定義一個結(jié)構(gòu)數(shù)組,用于標(biāo)識數(shù)組中每個節(jié)點是否有效(是否存儲內(nèi)容) /// </summary> internal volatile VolatileBool[] m_state; //指針,指向下一個節(jié)點數(shù)組 //如果是最后一個節(jié)點,則節(jié)點為空 private volatile Segment m_next; /// <summary> /// 索引,用來存儲鏈表的長度 /// </summary> internal readonly long m_index; /// <summary> /// 用來標(biāo)識隊列頭-數(shù)組彈出索引 /// </summary> private volatile int m_low; /// <summary> /// 用來標(biāo)識隊列尾-數(shù)組最新存儲位置 /// </summary> private volatile int m_high; /// <summary> /// 用來標(biāo)識隊列 /// </summary> private volatile MyConcurrentQueue<T> m_source; /// <summary> /// 實例化鏈節(jié)點 /// </summary> internal Segment(long index, MyConcurrentQueue<T> source) { m_array = new T[SEGMENT_SIZE]; m_state = new VolatileBool[SEGMENT_SIZE]; //all initialized to false m_high = -1; m_index = index; m_source = source; } /// <summary> /// 鏈表的下一個節(jié)點 /// </summary> internal Segment Next { get { return m_next; } } /// <summary> /// 如果當(dāng)前節(jié)點數(shù)組為空返回true, /// </summary> internal bool IsEmpty { get { return (Low > High); } } /// <summary> /// 非安全添加方法(無判斷數(shù)組長度) /// </summary> /// <param name="value"></param> internal void UnsafeAdd(T value) { m_high++; m_array[m_high] = value; m_state[m_high].m_value = true; } internal Segment UnsafeGrow() { Segment newSegment = new Segment(m_index + 1, m_source); m_next = newSegment; return newSegment; } /// <summary> /// 如果當(dāng)前數(shù)組滿了 >=32,則鏈擴展節(jié)點。 /// </summary> internal void Grow() { //重新船艦數(shù)組 Segment newSegment = new Segment(m_index + 1, m_source); //賦值給next指針 m_next = newSegment; //將節(jié)點添加到鏈 m_source.m_tail = m_next; } /// <summary> /// 在末尾添加元素 /// </summary> /// <param name="value">元素</param> /// <param name="tail">The tail.</param> /// <returns>如果附加元素,則為true;如果當(dāng)前數(shù)組已滿,則為false</returns> /// <remarks>如果附加指定的元素成功,并且在此之后數(shù)組滿了,在鏈上添加新節(jié)點(節(jié)點為32長度數(shù)組) </remarks> internal bool TryAppend(T value) { //如果數(shù)組已滿則跳出方法 if (m_high >= SEGMENT_SIZE - 1) { return false; } //局部變量初始化 int newhigh = SEGMENT_SIZE; try { } finally { //原子遞增 newhigh = Interlocked.Increment(ref m_high); if (newhigh <= SEGMENT_SIZE - 1) { m_array[newhigh] = value; m_state[newhigh].m_value = true; } //如果數(shù)組滿了,則擴展鏈節(jié)點。 if (newhigh == SEGMENT_SIZE - 1) { Grow(); } } //如果 newhigh <= SEGMENT_SIZE-1, 這意味著當(dāng)前線程成功地占據(jù)了一個位置 return newhigh <= SEGMENT_SIZE - 1; } /// <summary> /// 嘗試從鏈的頭部數(shù)組刪除節(jié)點 /// </summary> /// <param name="result"></param> /// <returns></returns> internal bool TryRemove(out T result) { SpinWait spin = new SpinWait(); int lowLocal = Low, highLocal = High; while (lowLocal <= highLocal) { //獲取隊頭索引 if (Interlocked.CompareExchange(ref m_low, lowLocal + 1, lowLocal) == lowLocal) { //如果要彈出隊列的值不可用,說明這個位置被并行線程獲取到了權(quán)限,但是值還未寫入。 //通過線程自旋等待值寫入 SpinWait spinLocal = new SpinWait(); while (!m_state[lowLocal].m_value) { spinLocal.SpinOnce(); } //取出值 result = m_array[lowLocal]; // 如果沒有其他線程讀?。℅etEnumerator()、ToList()) 執(zhí)行刪除 // 如 TryPeek 的時候m_numSnapshotTakers會在進入方法體時++,在出方法體-- // 清空該索引下的值 if (m_source.m_numSnapshotTakers <= 0) m_array[lowLocal] = default(T); //如果說lowLocal+1 = 32 說明當(dāng)前鏈節(jié)點的數(shù)組已經(jīng)全部出隊 if (lowLocal + 1 >= SEGMENT_SIZE) { //由于lowLocal <= highLocal成立 //lowLocal + 1 >= SEGMENT_SIZE 如果成立 ,且m_next == null 成立, //說明在此時有其他線程正在做擴展鏈結(jié)構(gòu) //那么當(dāng)前線程需要等待其他線程完成擴展鏈表,再做出隊操作。 spinLocal = new SpinWait(); while (m_next == null) { spinLocal.SpinOnce(); } m_source.m_head = m_next; } return true; } else { //此時說明 當(dāng)前線程競爭資源失敗,做短暫自旋后繼續(xù)競爭資源 spin.SpinOnce(); lowLocal = Low; highLocal = High; } } //失敗的情況下返回空值 result = default(T); return false; } /// <summary> /// 嘗試獲取隊列頭節(jié)點元素 /// </summary> internal bool TryPeek(out T result) { result = default(T); int lowLocal = Low; //校驗當(dāng)前隊列是否正確 if (lowLocal > High) return false; SpinWait spin = new SpinWait(); //如果頭節(jié)點無效,則說明當(dāng)前節(jié)點被其他線程占用,并在做寫入操作, //需要等待其他線程寫入后再執(zhí)行讀取操作 while (!m_state[lowLocal].m_value) { spin.SpinOnce(); } result = m_array[lowLocal]; return true; } /// <summary> /// 返回隊列首位置 /// </summary> internal int Low { get { return Math.Min(m_low, SEGMENT_SIZE); } } /// <summary> /// 獲取隊列長度 /// </summary> internal int High { get { //如果m_high>SEGMENT_SIZE,則表示超出范圍,我們應(yīng)該返回 SEGMENT_SIZE-1 return Math.Min(m_high, SEGMENT_SIZE - 1); } } } }
/// <summary> /// 結(jié)構(gòu)-用來存儲整數(shù)組每個索引上是否存儲值 /// </summary> struct VolatileBool { public VolatileBool(bool value) { m_value = value; } public volatile bool m_value; }
代碼通篇看下來有些長(已經(jīng)精簡了很多,只實現(xiàn)入隊、出隊、與查看下一個出隊的值),不知道有多少人能翻到這里~
說明:
1、TryAppend方法通過Interlocked.Increment()原子遞增方法獲取下一個數(shù)組存儲點,通過比對32判斷鏈?zhǔn)欠裥枰黾酉乱粋€鏈節(jié)點,也就是說,鏈的存儲空間每次擴展為32個存儲位置。
2、TryRemove方法通過 Interlocked.CompareExchange()方法來判斷當(dāng)前是否有并行線程在寫入,如果有則通過 while循環(huán) SpinWait類的SpinOnce()方法實現(xiàn)等待寫入完成后,再做刪除;特別說明,判斷是否寫入是靠VolatileBool結(jié)構(gòu)來實現(xiàn)的,每個鏈表的每個節(jié)點在存儲值的同時每個存儲都對應(yīng)一個VolatileBool結(jié)構(gòu)用來標(biāo)識當(dāng)前寫入點是否成功寫入。特殊情況,如果當(dāng)前鏈節(jié)點的數(shù)組已經(jīng)空了,則需要pinWait類的SpinOnce()簡短的自旋等待并行的寫入方法完成擴展鏈后,再做刪除。
3、TryPeek方法,同樣會判斷要獲取的元素是否已經(jīng)成功寫入(不成功則說明并行線程還未完成寫入),如果未完成,則通過 while pinWait類的SpinOnce()來等待寫入完成后,再讀取元素內(nèi)容。
現(xiàn)在代碼已經(jīng)看完了,來試下:
MyConcurrentQueue<string> myConcurrentQueue = new MyConcurrentQueue<string>(); for (int i = 0; i < 67; i++) { myConcurrentQueue.Enqueue($"第{i}位"); Console.WriteLine($"總數(shù):{myConcurrentQueue.Count}"); } myConcurrentQueue.TryPeek(out string rs); Console.WriteLine($"TryPeek 總數(shù):{myConcurrentQueue.Count}"); for (int i = 0; i < 34; i++) { myConcurrentQueue.TryDequeue(out string result0); Console.WriteLine($"TryDequeue 總數(shù):{myConcurrentQueue.Count}"); } Console.ReadKey();
打印:
head.Low:0,tail.High:0,head.m_index:0,tail.m_index:0 總數(shù):1 head.Low:0,tail.High:1,head.m_index:0,tail.m_index:0 總數(shù):2 head.Low:0,tail.High:2,head.m_index:0,tail.m_index:0 總數(shù):3 head.Low:0,tail.High:3,head.m_index:0,tail.m_index:0 總數(shù):4 head.Low:0,tail.High:4,head.m_index:0,tail.m_index:0 總數(shù):5 head.Low:0,tail.High:5,head.m_index:0,tail.m_index:0 總數(shù):6 head.Low:0,tail.High:6,head.m_index:0,tail.m_index:0 總數(shù):7 head.Low:0,tail.High:7,head.m_index:0,tail.m_index:0 總數(shù):8 head.Low:0,tail.High:8,head.m_index:0,tail.m_index:0 總數(shù):9 head.Low:0,tail.High:9,head.m_index:0,tail.m_index:0 總數(shù):10 head.Low:0,tail.High:10,head.m_index:0,tail.m_index:0 總數(shù):11 head.Low:0,tail.High:11,head.m_index:0,tail.m_index:0 總數(shù):12 head.Low:0,tail.High:12,head.m_index:0,tail.m_index:0 總數(shù):13 head.Low:0,tail.High:13,head.m_index:0,tail.m_index:0 總數(shù):14 head.Low:0,tail.High:14,head.m_index:0,tail.m_index:0 總數(shù):15 head.Low:0,tail.High:15,head.m_index:0,tail.m_index:0 總數(shù):16 head.Low:0,tail.High:16,head.m_index:0,tail.m_index:0 總數(shù):17 head.Low:0,tail.High:17,head.m_index:0,tail.m_index:0 總數(shù):18 head.Low:0,tail.High:18,head.m_index:0,tail.m_index:0 總數(shù):19 head.Low:0,tail.High:19,head.m_index:0,tail.m_index:0 總數(shù):20 head.Low:0,tail.High:20,head.m_index:0,tail.m_index:0 總數(shù):21 head.Low:0,tail.High:21,head.m_index:0,tail.m_index:0 總數(shù):22 head.Low:0,tail.High:22,head.m_index:0,tail.m_index:0 總數(shù):23 head.Low:0,tail.High:23,head.m_index:0,tail.m_index:0 總數(shù):24 head.Low:0,tail.High:24,head.m_index:0,tail.m_index:0 總數(shù):25 head.Low:0,tail.High:25,head.m_index:0,tail.m_index:0 總數(shù):26 head.Low:0,tail.High:26,head.m_index:0,tail.m_index:0 總數(shù):27 head.Low:0,tail.High:27,head.m_index:0,tail.m_index:0 總數(shù):28 head.Low:0,tail.High:28,head.m_index:0,tail.m_index:0 總數(shù):29 head.Low:0,tail.High:29,head.m_index:0,tail.m_index:0 總數(shù):30 head.Low:0,tail.High:30,head.m_index:0,tail.m_index:0 總數(shù):31 head.Low:0,tail.High:-1,head.m_index:0,tail.m_index:1 總數(shù):32 head.Low:0,tail.High:0,head.m_index:0,tail.m_index:1 總數(shù):33 head.Low:0,tail.High:1,head.m_index:0,tail.m_index:1 總數(shù):34 head.Low:0,tail.High:2,head.m_index:0,tail.m_index:1 總數(shù):35 head.Low:0,tail.High:3,head.m_index:0,tail.m_index:1 總數(shù):36 head.Low:0,tail.High:4,head.m_index:0,tail.m_index:1 總數(shù):37 head.Low:0,tail.High:5,head.m_index:0,tail.m_index:1 總數(shù):38 head.Low:0,tail.High:6,head.m_index:0,tail.m_index:1 總數(shù):39 head.Low:0,tail.High:7,head.m_index:0,tail.m_index:1 總數(shù):40 head.Low:0,tail.High:8,head.m_index:0,tail.m_index:1 總數(shù):41 head.Low:0,tail.High:9,head.m_index:0,tail.m_index:1 總數(shù):42 head.Low:0,tail.High:10,head.m_index:0,tail.m_index:1 總數(shù):43 head.Low:0,tail.High:11,head.m_index:0,tail.m_index:1 總數(shù):44 head.Low:0,tail.High:12,head.m_index:0,tail.m_index:1 總數(shù):45 head.Low:0,tail.High:13,head.m_index:0,tail.m_index:1 總數(shù):46 head.Low:0,tail.High:14,head.m_index:0,tail.m_index:1 總數(shù):47 head.Low:0,tail.High:15,head.m_index:0,tail.m_index:1 總數(shù):48 head.Low:0,tail.High:16,head.m_index:0,tail.m_index:1 總數(shù):49 head.Low:0,tail.High:17,head.m_index:0,tail.m_index:1 總數(shù):50 head.Low:0,tail.High:18,head.m_index:0,tail.m_index:1 總數(shù):51 head.Low:0,tail.High:19,head.m_index:0,tail.m_index:1 總數(shù):52 head.Low:0,tail.High:20,head.m_index:0,tail.m_index:1 總數(shù):53 head.Low:0,tail.High:21,head.m_index:0,tail.m_index:1 總數(shù):54 head.Low:0,tail.High:22,head.m_index:0,tail.m_index:1 總數(shù):55 head.Low:0,tail.High:23,head.m_index:0,tail.m_index:1 總數(shù):56 head.Low:0,tail.High:24,head.m_index:0,tail.m_index:1 總數(shù):57 head.Low:0,tail.High:25,head.m_index:0,tail.m_index:1 總數(shù):58 head.Low:0,tail.High:26,head.m_index:0,tail.m_index:1 總數(shù):59 head.Low:0,tail.High:27,head.m_index:0,tail.m_index:1 總數(shù):60 head.Low:0,tail.High:28,head.m_index:0,tail.m_index:1 總數(shù):61 head.Low:0,tail.High:29,head.m_index:0,tail.m_index:1 總數(shù):62 head.Low:0,tail.High:30,head.m_index:0,tail.m_index:1 總數(shù):63 head.Low:0,tail.High:-1,head.m_index:0,tail.m_index:2 總數(shù):64 head.Low:0,tail.High:0,head.m_index:0,tail.m_index:2 總數(shù):65 head.Low:0,tail.High:1,head.m_index:0,tail.m_index:2 總數(shù):66 head.Low:0,tail.High:2,head.m_index:0,tail.m_index:2 總數(shù):67 head.Low:0,tail.High:2,head.m_index:0,tail.m_index:2 TryPeek 總數(shù):67 head.Low:1,tail.High:2,head.m_index:0,tail.m_index:2 TryDequeue 總數(shù):66 head.Low:2,tail.High:2,head.m_index:0,tail.m_index:2 TryDequeue 總數(shù):65 head.Low:3,tail.High:2,head.m_index:0,tail.m_index:2 TryDequeue 總數(shù):64 head.Low:4,tail.High:2,head.m_index:0,tail.m_index:2 TryDequeue 總數(shù):63 head.Low:5,tail.High:2,head.m_index:0,tail.m_index:2 TryDequeue 總數(shù):62 head.Low:6,tail.High:2,head.m_index:0,tail.m_index:2 TryDequeue 總數(shù):61 head.Low:7,tail.High:2,head.m_index:0,tail.m_index:2 TryDequeue 總數(shù):60 head.Low:8,tail.High:2,head.m_index:0,tail.m_index:2 TryDequeue 總數(shù):59 head.Low:9,tail.High:2,head.m_index:0,tail.m_index:2 TryDequeue 總數(shù):58 head.Low:10,tail.High:2,head.m_index:0,tail.m_index:2 TryDequeue 總數(shù):57 head.Low:11,tail.High:2,head.m_index:0,tail.m_index:2 TryDequeue 總數(shù):56 head.Low:12,tail.High:2,head.m_index:0,tail.m_index:2 TryDequeue 總數(shù):55 head.Low:13,tail.High:2,head.m_index:0,tail.m_index:2 TryDequeue 總數(shù):54 head.Low:14,tail.High:2,head.m_index:0,tail.m_index:2 TryDequeue 總數(shù):53 head.Low:15,tail.High:2,head.m_index:0,tail.m_index:2 TryDequeue 總數(shù):52 head.Low:16,tail.High:2,head.m_index:0,tail.m_index:2 TryDequeue 總數(shù):51 head.Low:17,tail.High:2,head.m_index:0,tail.m_index:2 TryDequeue 總數(shù):50 head.Low:18,tail.High:2,head.m_index:0,tail.m_index:2 TryDequeue 總數(shù):49 head.Low:19,tail.High:2,head.m_index:0,tail.m_index:2 TryDequeue 總數(shù):48 head.Low:20,tail.High:2,head.m_index:0,tail.m_index:2 TryDequeue 總數(shù):47 head.Low:21,tail.High:2,head.m_index:0,tail.m_index:2 TryDequeue 總數(shù):46 head.Low:22,tail.High:2,head.m_index:0,tail.m_index:2 TryDequeue 總數(shù):45 head.Low:23,tail.High:2,head.m_index:0,tail.m_index:2 TryDequeue 總數(shù):44 head.Low:24,tail.High:2,head.m_index:0,tail.m_index:2 TryDequeue 總數(shù):43 head.Low:25,tail.High:2,head.m_index:0,tail.m_index:2 TryDequeue 總數(shù):42 head.Low:26,tail.High:2,head.m_index:0,tail.m_index:2 TryDequeue 總數(shù):41 head.Low:27,tail.High:2,head.m_index:0,tail.m_index:2 TryDequeue 總數(shù):40 head.Low:28,tail.High:2,head.m_index:0,tail.m_index:2 TryDequeue 總數(shù):39 head.Low:29,tail.High:2,head.m_index:0,tail.m_index:2 TryDequeue 總數(shù):38 head.Low:30,tail.High:2,head.m_index:0,tail.m_index:2 TryDequeue 總數(shù):37 head.Low:31,tail.High:2,head.m_index:0,tail.m_index:2 TryDequeue 總數(shù):36 head.Low:0,tail.High:2,head.m_index:1,tail.m_index:2 TryDequeue 總數(shù):35 head.Low:1,tail.High:2,head.m_index:1,tail.m_index:2 TryDequeue 總數(shù):34 head.Low:2,tail.High:2,head.m_index:1,tail.m_index:2 TryDequeue 總數(shù):33
有時間希望大家能將代碼跑一下,相信會更明白其中的原理。
以上就是c# 線程安全隊列的用法原理及使用示例的詳細內(nèi)容,更多關(guān)于c# 線程安全隊列的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C#正則表達式分解和轉(zhuǎn)換IP地址實例(C#正則表達式大全 c#正則表達式語法)
這是我發(fā)了不少時間整理的C#的正則表達式,新手朋友注意一定要手冊一下哦,這樣可以節(jié)省很多寫代碼的時間。下面進行了簡單總結(jié)2013-12-12