什么是线程安全?

答:线程安全是多线程编程时的计算机程序代码中的一个概念。在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。

前面几篇写的线性结构,在多线程并行的情况下会出现共享数据会线程间读取与写入不一直的情况,为了解决这种情况,通常会使用锁来解决,也就是将并行改为串行。但是在使用穿行违背了使用多线程并发的初衷,这种情况下我们可以考虑采用线程安全结构。

先看下线程安全队列的用法:

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();

现在我们看下线程安全队列的实现方式:(参考自:.net framework 4.8),核心代码全部做了注释。

总的来说,(总结语放到前面,防止代码篇幅太大,同志们没有耐心翻到最底下~)

1、线程安全队列通过SpinWait自旋类来实现等待并行线程完成与Interlocked原子操作类计数实现的。

2、线程安全队列通过单向链表实现的,链的节点为长度32的数组,通过记录链的头节点与尾节点、以及队列的头尾实现队列的存储与入队、出队操作的。

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>
/// 链尾部节点
/// </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>
/// 判断当前链是否为空
/// </summary>
public bool IsEmpty
{
get
{
Segment head = m_head;
if (!head.IsEmpty)
//如果头不为空,则链非空
return false;
else if (head.Next == null)
//如果头节点的下一个节点为空,且为链尾,
return true;
else
//如果头节点为空且不是最后一个节点 ,则标识另一个线程正在写入该数组
//等待中..
{
SpinWait spin = new SpinWait();
while (head.IsEmpty)
{
//此时为空
if (head.Next == null)
return true;
//否则标识正在有线程占用写入
//线程循环一次
spin.SpinOnce();
head = m_head;
}
return false;
}
}
}
/// <summary>
/// 用来判断链是否在变化
/// </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}");
//通过循环来保证值不再更改(也就是说并行线程操作结束)
//保证线程串行核心的判断逻辑
while (
//头尾发生变化
head != m_head || tail != m_tail
//如果队列头、尾索引发生变化
|| 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>
/// 获取总数
/// </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;
}
//头节点长度
int count = SEGMENT_SIZE - headLow;
//加上中间其他节点长度
count += SEGMENT_SIZE * ((int)(tail.m_index - head.m_index - 1));
//加上尾节点长度
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>
/// 暂未实现
/// </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>
/// 尝试删除节点
/// </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)
{
//首先从头节点看一下第一个节点是否存在
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>
/// 为线程安全队列提供一个 单向链表,
/// 链表的每个节点存储长度为32的数组
/// </summary>
private class Segment
{
/// <summary>
/// 定义一个数组,用于存储每个节点的内容
/// </summary>
internal volatile T[] m_array;
/// <summary>
/// 定义一个结构数组,用于标识数组中每个节点是否有效(是否存储内容)
/// </summary>
internal volatile VolatileBool[] m_state;
//指针,指向下一个节点数组
//如果是最后一个节点,则节点为空
private volatile Segment m_next;
/// <summary>
/// 索引,用来存储链表的长度
/// </summary>
internal readonly long m_index;
/// <summary>
/// 用来标识队列头-数组弹出索引
/// </summary>
private volatile int m_low;
/// <summary>
/// 用来标识队列尾-数组最新存储位置
/// </summary>
private volatile int m_high;
/// <summary>
/// 用来标识队列
/// </summary>
private volatile MyConcurrentQueue<T> m_source;
/// <summary>
/// 实例化链节点
/// </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>
/// 链表的下一个节点
/// </summary>
internal Segment Next
{
get { return m_next; }
}
/// <summary>
/// 如果当前节点数组为空返回true,
/// </summary>
internal bool IsEmpty
{
get { return (Low > High); }
}
/// <summary>
/// 非安全添加方法(无判断数组长度)
/// </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>
/// 如果当前数组满了 >=32,则链扩展节点。
/// </summary>
internal void Grow()
{
//重新船舰数组
Segment newSegment = new Segment(m_index + 1, m_source);
//赋值给next指针
m_next = newSegment;
//将节点添加到链
m_source.m_tail = m_next;
} /// <summary>
/// 在末尾添加元素
/// </summary>
/// <param name="value">元素</param>
/// <param name="tail">The tail.</param>
/// <returns>如果附加元素,则为true;如果当前数组已满,则为false</returns>
/// <remarks>如果附加指定的元素成功,并且在此之后数组满了,在链上添加新节点(节点为32长度数组) </remarks>
internal bool TryAppend(T value)
{
//如果数组已满则跳出方法
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;
}
//如果数组满了,则扩展链节点。
if (newhigh == SEGMENT_SIZE - 1)
{
Grow();
}
}
//如果 newhigh <= SEGMENT_SIZE-1, 这意味着当前线程成功地占据了一个位置
return newhigh <= SEGMENT_SIZE - 1;
} /// <summary>
/// 尝试从链的头部数组删除节点
/// </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)
{
//如果要弹出队列的值不可用,说明这个位置被并行线程获取到了权限,但是值还未写入。
//通过线程自旋等待值写入
SpinWait spinLocal = new SpinWait();
while (!m_state[lowLocal].m_value)
{
spinLocal.SpinOnce();
}
//取出值
result = m_array[lowLocal];
// 如果没有其他线程读取(GetEnumerator()、ToList()) 执行删除
// 如 TryPeek 的时候m_numSnapshotTakers会在进入方法体时++,在出方法体--
// 清空该索引下的值
if (m_source.m_numSnapshotTakers <= 0)
m_array[lowLocal] = default(T);
//如果说lowLocal+1 = 32 说明当前链节点的数组已经全部出队
if (lowLocal + 1 >= SEGMENT_SIZE)
{
//由于lowLocal <= highLocal成立
//lowLocal + 1 >= SEGMENT_SIZE 如果成立 ,且m_next == null 成立,
//说明在此时有其他线程正在做扩展链结构
//那么当前线程需要等待其他线程完成扩展链表,再做出队操作。
spinLocal = new SpinWait();
while (m_next == null)
{
spinLocal.SpinOnce();
}
m_source.m_head = m_next;
}
return true;
}
else
{
//此时说明 当前线程竞争资源失败,做短暂自旋后继续竞争资源
spin.SpinOnce();
lowLocal = Low; highLocal = High;
}
}
//失败的情况下返回空值
result = default(T);
return false;
}
/// <summary>
/// 尝试获取队列头节点元素
/// </summary>
internal bool TryPeek(out T result)
{
result = default(T);
int lowLocal = Low;
//校验当前队列是否正确
if (lowLocal > High)
return false;
SpinWait spin = new SpinWait();
//如果头节点无效,则说明当前节点被其他线程占用,并在做写入操作,
//需要等待其他线程写入后再执行读取操作
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,则表示超出范围,我们应该返回 SEGMENT_SIZE-1
return Math.Min(m_high, SEGMENT_SIZE - 1);
}
}
}
}
/// <summary>
/// 结构-用来存储整数组每个索引上是否存储值
/// </summary>
struct VolatileBool
{
public VolatileBool(bool value)
{
m_value = value;
}
public volatile bool m_value;
}

代码通篇看下来有些长(已经精简了很多,只实现入队、出队、与查看下一个出队的值),不知道有多少人能翻到这里~

说明:

1、TryAppend方法通过Interlocked.Increment()原子递增方法获取下一个数组存储点,通过比对32判断链是否需要增加下一个链节点,也就是说,链的存储空间每次扩展为32个存储位置。

2、TryRemove方法通过 Interlocked.CompareExchange()方法来判断当前是否有并行线程在写入,如果有则通过 while循环 SpinWait类的SpinOnce()方法实现等待写入完成后,再做删除;特别说明,判断是否写入是靠VolatileBool结构来实现的,每个链表的每个节点在存储值的同时每个存储都对应一个VolatileBool结构用来标识当前写入点是否成功写入。特殊情况,如果当前链节点的数组已经空了,则需要pinWait类的SpinOnce()简短的自旋等待并行的写入方法完成扩展链后,再做删除。

3、TryPeek方法,同样会判断要获取的元素是否已经成功写入(不成功则说明并行线程还未完成写入),如果未完成,则通过 while pinWait类的SpinOnce()来等待写入完成后,再读取元素内容。

现在代码已经看完了,来试下:

MyConcurrentQueue<string> myConcurrentQueue = new MyConcurrentQueue<string>();
for (int i = 0; i < 67; i++)
{
myConcurrentQueue.Enqueue($"第{i}位");
Console.WriteLine($"总数:{myConcurrentQueue.Count}");
} myConcurrentQueue.TryPeek(out string rs);
Console.WriteLine($"TryPeek 总数:{myConcurrentQueue.Count}");
for (int i = 0; i < 34; i++)
{
myConcurrentQueue.TryDequeue(out string result0);
Console.WriteLine($"TryDequeue 总数:{myConcurrentQueue.Count}");
}
Console.ReadKey();

打印:

head.Low:0,tail.High:0,head.m_index:0,tail.m_index:0
总数:1
head.Low:0,tail.High:1,head.m_index:0,tail.m_index:0
总数:2
head.Low:0,tail.High:2,head.m_index:0,tail.m_index:0
总数:3
head.Low:0,tail.High:3,head.m_index:0,tail.m_index:0
总数:4
head.Low:0,tail.High:4,head.m_index:0,tail.m_index:0
总数:5
head.Low:0,tail.High:5,head.m_index:0,tail.m_index:0
总数:6
head.Low:0,tail.High:6,head.m_index:0,tail.m_index:0
总数:7
head.Low:0,tail.High:7,head.m_index:0,tail.m_index:0
总数:8
head.Low:0,tail.High:8,head.m_index:0,tail.m_index:0
总数:9
head.Low:0,tail.High:9,head.m_index:0,tail.m_index:0
总数:10
head.Low:0,tail.High:10,head.m_index:0,tail.m_index:0
总数:11
head.Low:0,tail.High:11,head.m_index:0,tail.m_index:0
总数:12
head.Low:0,tail.High:12,head.m_index:0,tail.m_index:0
总数:13
head.Low:0,tail.High:13,head.m_index:0,tail.m_index:0
总数:14
head.Low:0,tail.High:14,head.m_index:0,tail.m_index:0
总数:15
head.Low:0,tail.High:15,head.m_index:0,tail.m_index:0
总数:16
head.Low:0,tail.High:16,head.m_index:0,tail.m_index:0
总数:17
head.Low:0,tail.High:17,head.m_index:0,tail.m_index:0
总数:18
head.Low:0,tail.High:18,head.m_index:0,tail.m_index:0
总数:19
head.Low:0,tail.High:19,head.m_index:0,tail.m_index:0
总数:20
head.Low:0,tail.High:20,head.m_index:0,tail.m_index:0
总数:21
head.Low:0,tail.High:21,head.m_index:0,tail.m_index:0
总数:22
head.Low:0,tail.High:22,head.m_index:0,tail.m_index:0
总数:23
head.Low:0,tail.High:23,head.m_index:0,tail.m_index:0
总数:24
head.Low:0,tail.High:24,head.m_index:0,tail.m_index:0
总数:25
head.Low:0,tail.High:25,head.m_index:0,tail.m_index:0
总数:26
head.Low:0,tail.High:26,head.m_index:0,tail.m_index:0
总数:27
head.Low:0,tail.High:27,head.m_index:0,tail.m_index:0
总数:28
head.Low:0,tail.High:28,head.m_index:0,tail.m_index:0
总数:29
head.Low:0,tail.High:29,head.m_index:0,tail.m_index:0
总数:30
head.Low:0,tail.High:30,head.m_index:0,tail.m_index:0
总数:31
head.Low:0,tail.High:-1,head.m_index:0,tail.m_index:1
总数:32
head.Low:0,tail.High:0,head.m_index:0,tail.m_index:1
总数:33
head.Low:0,tail.High:1,head.m_index:0,tail.m_index:1
总数:34
head.Low:0,tail.High:2,head.m_index:0,tail.m_index:1
总数:35
head.Low:0,tail.High:3,head.m_index:0,tail.m_index:1
总数:36
head.Low:0,tail.High:4,head.m_index:0,tail.m_index:1
总数:37
head.Low:0,tail.High:5,head.m_index:0,tail.m_index:1
总数:38
head.Low:0,tail.High:6,head.m_index:0,tail.m_index:1
总数:39
head.Low:0,tail.High:7,head.m_index:0,tail.m_index:1
总数:40
head.Low:0,tail.High:8,head.m_index:0,tail.m_index:1
总数:41
head.Low:0,tail.High:9,head.m_index:0,tail.m_index:1
总数:42
head.Low:0,tail.High:10,head.m_index:0,tail.m_index:1
总数:43
head.Low:0,tail.High:11,head.m_index:0,tail.m_index:1
总数:44
head.Low:0,tail.High:12,head.m_index:0,tail.m_index:1
总数:45
head.Low:0,tail.High:13,head.m_index:0,tail.m_index:1
总数:46
head.Low:0,tail.High:14,head.m_index:0,tail.m_index:1
总数:47
head.Low:0,tail.High:15,head.m_index:0,tail.m_index:1
总数:48
head.Low:0,tail.High:16,head.m_index:0,tail.m_index:1
总数:49
head.Low:0,tail.High:17,head.m_index:0,tail.m_index:1
总数:50
head.Low:0,tail.High:18,head.m_index:0,tail.m_index:1
总数:51
head.Low:0,tail.High:19,head.m_index:0,tail.m_index:1
总数:52
head.Low:0,tail.High:20,head.m_index:0,tail.m_index:1
总数:53
head.Low:0,tail.High:21,head.m_index:0,tail.m_index:1
总数:54
head.Low:0,tail.High:22,head.m_index:0,tail.m_index:1
总数:55
head.Low:0,tail.High:23,head.m_index:0,tail.m_index:1
总数:56
head.Low:0,tail.High:24,head.m_index:0,tail.m_index:1
总数:57
head.Low:0,tail.High:25,head.m_index:0,tail.m_index:1
总数:58
head.Low:0,tail.High:26,head.m_index:0,tail.m_index:1
总数:59
head.Low:0,tail.High:27,head.m_index:0,tail.m_index:1
总数:60
head.Low:0,tail.High:28,head.m_index:0,tail.m_index:1
总数:61
head.Low:0,tail.High:29,head.m_index:0,tail.m_index:1
总数:62
head.Low:0,tail.High:30,head.m_index:0,tail.m_index:1
总数:63
head.Low:0,tail.High:-1,head.m_index:0,tail.m_index:2
总数:64
head.Low:0,tail.High:0,head.m_index:0,tail.m_index:2
总数:65
head.Low:0,tail.High:1,head.m_index:0,tail.m_index:2
总数:66
head.Low:0,tail.High:2,head.m_index:0,tail.m_index:2
总数:67
head.Low:0,tail.High:2,head.m_index:0,tail.m_index:2
TryPeek 总数:67
head.Low:1,tail.High:2,head.m_index:0,tail.m_index:2
TryDequeue 总数:66
head.Low:2,tail.High:2,head.m_index:0,tail.m_index:2
TryDequeue 总数:65
head.Low:3,tail.High:2,head.m_index:0,tail.m_index:2
TryDequeue 总数:64
head.Low:4,tail.High:2,head.m_index:0,tail.m_index:2
TryDequeue 总数:63
head.Low:5,tail.High:2,head.m_index:0,tail.m_index:2
TryDequeue 总数:62
head.Low:6,tail.High:2,head.m_index:0,tail.m_index:2
TryDequeue 总数:61
head.Low:7,tail.High:2,head.m_index:0,tail.m_index:2
TryDequeue 总数:60
head.Low:8,tail.High:2,head.m_index:0,tail.m_index:2
TryDequeue 总数:59
head.Low:9,tail.High:2,head.m_index:0,tail.m_index:2
TryDequeue 总数:58
head.Low:10,tail.High:2,head.m_index:0,tail.m_index:2
TryDequeue 总数:57
head.Low:11,tail.High:2,head.m_index:0,tail.m_index:2
TryDequeue 总数:56
head.Low:12,tail.High:2,head.m_index:0,tail.m_index:2
TryDequeue 总数:55
head.Low:13,tail.High:2,head.m_index:0,tail.m_index:2
TryDequeue 总数:54
head.Low:14,tail.High:2,head.m_index:0,tail.m_index:2
TryDequeue 总数:53
head.Low:15,tail.High:2,head.m_index:0,tail.m_index:2
TryDequeue 总数:52
head.Low:16,tail.High:2,head.m_index:0,tail.m_index:2
TryDequeue 总数:51
head.Low:17,tail.High:2,head.m_index:0,tail.m_index:2
TryDequeue 总数:50
head.Low:18,tail.High:2,head.m_index:0,tail.m_index:2
TryDequeue 总数:49
head.Low:19,tail.High:2,head.m_index:0,tail.m_index:2
TryDequeue 总数:48
head.Low:20,tail.High:2,head.m_index:0,tail.m_index:2
TryDequeue 总数:47
head.Low:21,tail.High:2,head.m_index:0,tail.m_index:2
TryDequeue 总数:46
head.Low:22,tail.High:2,head.m_index:0,tail.m_index:2
TryDequeue 总数:45
head.Low:23,tail.High:2,head.m_index:0,tail.m_index:2
TryDequeue 总数:44
head.Low:24,tail.High:2,head.m_index:0,tail.m_index:2
TryDequeue 总数:43
head.Low:25,tail.High:2,head.m_index:0,tail.m_index:2
TryDequeue 总数:42
head.Low:26,tail.High:2,head.m_index:0,tail.m_index:2
TryDequeue 总数:41
head.Low:27,tail.High:2,head.m_index:0,tail.m_index:2
TryDequeue 总数:40
head.Low:28,tail.High:2,head.m_index:0,tail.m_index:2
TryDequeue 总数:39
head.Low:29,tail.High:2,head.m_index:0,tail.m_index:2
TryDequeue 总数:38
head.Low:30,tail.High:2,head.m_index:0,tail.m_index:2
TryDequeue 总数:37
head.Low:31,tail.High:2,head.m_index:0,tail.m_index:2
TryDequeue 总数:36
head.Low:0,tail.High:2,head.m_index:1,tail.m_index:2
TryDequeue 总数:35
head.Low:1,tail.High:2,head.m_index:1,tail.m_index:2
TryDequeue 总数:34
head.Low:2,tail.High:2,head.m_index:1,tail.m_index:2
TryDequeue 总数:33

有时间希望大家能将代码跑一下,相信会更明白其中的原理。

C#数据结构-线程安全队列的更多相关文章

  1. 线程池 队列 synchronized

    线程池 BlockingQueue synchronized volatile 本章从线程池到阻塞队列BlockingQueue.从BlockingQueue到synchronized 和 volat ...

  2. JS数据结构与算法-队列结构

    队列结构 一.认识队列 受限的线性结构: 我们已经学习了一种受限的线性结构:栈结构. 并且已经知道这种受限的数据结构对于解决某些特定问题,会有特别的 效果. 下面,我们再来学习另外一个受限的数据结构: ...

  3. javascript数据结构与算法---队列

    javascript数据结构与算法---队列 队列是一种列表,不同的是队列只能在队尾插入元素,在队首删除元素.队列用于存储按顺序排列的数据,先进先出,这点和栈不一样(后入先出).在栈中,最后入栈的元素 ...

  4. Linux多线程系列-2-条件变量的使用(线程安全队列的实现)

    多线程情况下,往往需要使用互斥变量来实现线程间的同步,实现资源正确共享. linux下使用如下变量和函数 //条件变量 pthread_cond_t int pthread_cond_init (pt ...

  5. 使用Condition Variables 实现一个线程安全队列

    使用Condition Variables实现一个线程安全队列 测试机: i7-4800MQ .7GHz, logical core, physical core, 8G memory, 256GB ...

  6. [数据结构与算法]队列Queue 的多种实现

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  7. C++数据结构之链式队列(Linked Queue)

    C++数据结构之链式队列,实现的基本思想和链式栈的实现差不多,比较不同的一点也是需要注意的一点是,链式队列的指向指针有两个,一个是队头指针(front),一个是队尾指针(rear),注意指针的指向是从 ...

  8. [数据结构]C语言队列的实现

    我个人把链表.队列.栈分为一类,然后图.树分为一类.(串不考虑),分类的理由就是每一类有规律可循,即你能通过修改极少数的代码把链表变成队列.栈.(这里我们不考虑其他诸如设计模式等因素),因此本贴在讲完 ...

  9. JavaScript数据结构和算法----队列

    前言 队列和栈很像,只是用了不同的原则.队列是遵循先进先出(FIFO)原则的一组有序的的项,队列在尾部添加新元素,从顶部移除元素.最新添加的元素必须必须排队在队列的,末尾.可以想象食堂排队买饭的样子. ...

随机推荐

  1. pwnable.kr-shellshock-witeup

    思路是:发现文件执行没什么好反馈显示结果的,于是看文件和权限,通过bash文件猜测可能存在破壳漏洞(CVE-2014-6271)漏洞,于是利用它并结合文件权限成功获得flag. 通过scp下载文件至本 ...

  2. Typora,你好!

    初识Typora 1.标题 一个井号+空格+回车 =一级标题 两个井号+空格+回车 =二级标题 三个井号+空格+回车 =三级标题 四个井号+空格+回车 =四级标题 快捷键的话: 按ctrl + 1 就 ...

  3. python下正则表达式的随笔记录

    使用了下正则的表达式: 目的:取出字符串中{}中的内容 最后使用的正则表达式为 {(.*?)} 先看   .*?  : 首先  .  是用来匹配字符串,但是只能匹配一次. 所以加上  *  ,可以让 ...

  4. Java知识系统回顾整理01基础06数组02初始化数组

    一.分配空间与赋值分步进行 分配空间与赋值分步进行 public class HelloWorld { public static void main(String[] args) { int[] a ...

  5. 【随笔---转载】xip.io

    http://xip.io/ wildcard DNS for everyone 今天看到一个老外发布的DNS小工具,XIP.IO.功能十分简单,就是将foo.bar.10.0.0.1.xip.io  ...

  6. c++模板中set(date st):t(st)中的:符号

    转载:https://zhidao.baidu.com/question/618119741512344012.html 半角冒号是构造函数里的初bai始化列表 开始du的标识. 如楼上所述: set ...

  7. visual studio 2015 Opencv4.0.1配置

    最近由于工作需要,要配置opencv,我的电脑vs的version是2015,在网上下载了最新的opencv 4.0.1 自己摸索总是很困难,网上的例子也比较多,但版本比较低,也不确定适不适合vs20 ...

  8. 【题解】Ehab the Xorcist

    \(\color{red}{Link}\) \(\color{blue}{\text{Solution:}}\) 题目要求构造一个最短的序列,使得异或和为\(u\),数列和为\(v\). 那么,因为是 ...

  9. Go 接口类型

    接口作用 Go语言中的接口是一种类型,类似于Python中的抽象基类. Go语言中使用接口来体现多态,是duck-type的一种体现. 如,只要一个东西会叫,会走,那么我们就可以将它定义为一个动物的接 ...

  10. RHSA-2017:2930-重要: 内核 安全和BUG修复更新(需要重启、存在EXP、本地提权、代码执行)

    [root@localhost ~]# cat /etc/redhat-release CentOS Linux release 7.2.1511 (Core) 修复命令: 使用root账号登陆She ...