这一章主要介绍了System.Collections.Concurrent下的几个类。
ConcurrentQueue<T>
并发队列。完全无锁,使用CAS(compare-and-swap)比较并交换和自旋重试来实现线程安全。
//加入队尾
public
void Enqueue(T item)
//尝试删除队头,并将元素通过out返回,返回值表示是否操作成功
public
bool TryDequeue(
out T result)
//尝试获取队头,通过out返回元素,返回值为代表是否操作成功
public
bool TryPeek(
out
ConcurrentStack<T>
并发栈,完全无锁,使用CAS(compare-and-swap)比较并交换和自旋重试来实现线程安全。
public
void Push(T item)
public
void PushRange(T[] items)
public
void PushRange(T[] items,
int startIndex,
int count)
public
bool TryPeek(
out T result)
public
bool TryPop(
out T result)
public
int TryPopRange(T[] items)
public
int TryPopRange(T[] items,
int startIndex,
int
ConcurrentBag<T>
这是一个无序的对象集合,而且支持对象重复。在同一线程下添加和删除效率高,又是需要锁。
public
void Add(T item)
public
bool TryPeek(
out T result)
public
bool TryTake(
out
ConcurrentDictionary<TKey,TValue>
并发的键值对,读是没有锁的,写会有细粒度的锁。
public TValue GetOrAdd(TKey key,TValue value)
public
bool TryAdd(TKey key,TValue value)
public
bool TryGetValue(TKey key,
out TValue value)
public
bool TryRemove(TKey key,
out TValue value)
public
bool
IProducerConsumer<T>与BlockingCollection<T>
IProducerConsumerCollection<T>是对生产者-消费者模型的一个操作抽象,BlockingCollection<T>是一个实现。
针对生产者消费者模型,给出了很多方便的功能。
构造的时候可以设置最大容量,当集合达到最大容量,添加请求会被阻塞。
添加完成的判断也不需要自己在维护标识符
更有GetConsumingEnumerable(),获取集合迭代器,可以使用foreach,如果集合为空着会阻塞,并等待添加新的元素,如果集合没有元素并且IsAddingCompleted为true那么循环终止,可以省去不必要的自旋。
还支持超时和取消功能
private
const
int NUM_SENTENCES
=
2000000;
private
static BlockingCollection
<
string
> _sentencesBC;
private
static BlockingCollection
<
string
> _capWordsInSentencesBC;
private
static BlockingCollection
<
string
> _finalSentencesBC;
private
static
void ProduceSentences(System.Threading.CancellationToken ct)
{
//...
if (
!_sentencesBC.TryAdd(newSentence,
2000, ct))
{
throw
new TimeoutException(...);
}
//...
_sentencesBC.CompleteAdding();
}
private
static
void CapitalizeWordsInSentences()
{
//...
while (
!_sentencesBC.IsCompleted)
{
string sentence;
if (_sentencesBC.TryTake(
out sentence))
{
_capWordsInSentencesBC.Add(...);
}
}
_capWordsInSentencesBC.CompleteAdding();
}
private
static
void RemoveLettersInSentences()
{
//...
foreach (var sentence
in _capWordsInSentencesBC.GetConsumingEnumerable())
{
_finalSentencesBC.Add(RemoveLetters(letterChars, sentence));
}
_finalSentencesBC.CompleteAdding();
}
static
void Main(
string[] args)
{
_sentencesBC
=
new BlockingCollection
<
string
>(NUM_SENTENCES);
_capWordsInSentencesBC
=
new BlockingCollection
<
string
>(NUM_SENTENCES);
_finalSentencesBC
=
new BlockingCollection
<
string
>(NUM_SENTENCES);
var cts
=
new System.Threading.CancellationTokenSource();
var ct
= cts.Token;
var deferredCancelTask
= Task.Factory.StartNew(()
=
>
{
System.Threading.Thread.Sleep(
500);
cts.Cancel();
});
Parallel.Invoke(
()
=
> ProduceSentences(ct),
()
=
> CapitalizeWordsInSentences(),
()
=
> RemoveLettersInSentences()
);
}
任务计数器
常常需要知道还在运行的Task的数量。所以需要对计数器进行原子的加减
可以在任务新建的时候使用System.Threading.Interlocked.Increment(ref tasksRunning)
在任务结束后System.Threading.Interlocked.Decrement(ref tasksRunning);
private
static
int tasksRunning
=
0;
//inti method
for (
int i
=
0; i
< taskMax; i
++)
{
System.Threading.Interlocked.Increment(
ref tasksRunning);
tasks[i]
= Task.Factory.StartNew(()
=
>
{
try
{
//...
}
finally
{
System.Threading.Interlocked.Decrement(
ref tasksRunning);
}
});
}
//other method
while ((tasksRunning
>
0))
{
//...
}
并发集合和不安全集合互转
并发集合可以结构一个IEnumerable接口的集合做构造函数参数。
例如
string[] _invalidHexValues
= {
"AF",
"BD",
"BF",
"CF",
"DA",
"FA",
"FE",
"FF" };
var invalidHexValuesStack
=
new ConcurrentStack
<
string
>(_invalidHexValues);
要把并发集合转成不安全的可以使用CopyTo ToArray等方法
volatile关键字
使用volatile可以确保在不同的线程中进行访问的时候,可以得到最新值。这些变量不会被编译器按照只在一个线程中的进行访问的假定进行优化。
private static volatile bool _flag = false;