.Net 知识点
Multi Thread (多线程)
多线程下解决资源竞争的7种方法
- 临界区 Lock
其实是临界区(Monitor)的一个语法糖,通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。
private static object obj = new object();
private static int lockInt;
private static void LockIntAdd()
{
for (var i = 0; i < runTimes; i++)
{
lock (obj)
{
lockInt++;
}
}
}
- 互斥量 Mutex
为协调共同对一个共享资源的单独访问而设计的。在System.Threading命名空间下,Mutex其实就是互斥量,互斥量不单单能处理多线程之间的资源竞争,还能处理进程之间的资源竞争,功能是比较强大的,但是开销也很大,性能比较低。
private static Mutex mutex = new Mutex();
private static int mutexInt;
private static void MutexIntAdd()
{
for (var i = 0; i < runTimes; i++)
{
mutex.WaitOne();
mutexInt++;
mutex.ReleaseMutex();
}
}
- 信号量 Semaphore
为控制一个具有有限数量用户资源而设计。
private static Semaphore sema = new Semaphore(1, 1);
private static int semaphoreInt;
private static void SemaphoreIntAdd()
{
for (var i = 0; i < runTimes; i++)
{
sema.WaitOne();
semaphoreInt++;
sema.Release();
}
}
- 事件 AutoResetEvent
用来通知线程有一些事件已发生,从而启动后继任务的开始。
public static AutoResetEvent autoResetEvent = new AutoResetEvent(true);
private static int autoResetEventInt;
private static void AutoResetEventIntAdd()
{
for (var i = 0; i < runTimes; i++)
{
if (autoResetEvent.WaitOne())
{
autoResetEventInt++;
autoResetEvent.Set();
}
}
}
- 读写锁 ReaderWriterLockSlim
这种锁允许在有其他程序正在写的情况下读取资源,所以如果资源允许脏读,用这个比较合适。
private static ReaderWriterLockSlim LockSlim = new ReaderWriterLockSlim();
private static int lockSlimInt;
private static void LockSlimIntAdd()
{
for (var i = 0; i < runTimes; i++)
{
LockSlim.EnterWriteLock();
lockSlimInt++;
LockSlim.ExitWriteLock();
}
}
- 原子锁 Interlocked.CompareExchange
通过原子操作Interlocked.CompareExchange实现“无锁”竞争。
private static int isLock;
private static int ceInt;
private static void CEIntAdd()
{
//long tmp = 0;
for (var i = 0; i < runTimes; i++)
{
while (Interlocked.CompareExchange(ref isLock, 1, 0) == 1) { Thread.Sleep(1); }
ceInt++;
Interlocked.Exchange(ref isLock, 0);
}
}
- 原子性操作 Interlocked.Increment
这是一种特例,野外原子性操作本身天生线程安全,所以无需加锁。
private static int atomicInt;
private static void AtomicIntAdd()
{
for (var i = 0; i < runTimes; i++)
{
Interlocked.Increment(ref atomicInt);
}
}
- 使用ConCurrent线程安全的集合
- ConCurrentQueue
- Enqueue:在队尾插入元素
- TryDequeue:尝试删除队头元素,并通过out参数返回
- TryPeek:尝试将对头元素通过out参数返回,但不删除该元素
- ConcurrentStack
- Push:向栈顶插入元素
- TryPop:从栈顶弹出元素,并且通过out 参数返回
- TryPeek:返回栈顶元素,但不弹出
- ConcurrentDictionary
- AddOrUpdate:如果键不存在,方法会在容器中添加新的键和值,如果存在,则更新现有的键和值。
- GetOrAdd:如果键不存在,方法会向容器中添加新的键和值,如果存在则返回现有的值,并不添加新值。
- TryAdd:尝试在容器中添加新的键和值。
- TryGetValue:尝试根据指定的键获得值。
- TryRemove:尝试删除指定的键。
- TryUpdate:有条件的更新当前键所对应的值。
- GetEnumerator:返回一个能够遍历整个容器的枚举器
- ConcurrentBag 一个无序的数据结构集,当不需要考虑顺序时非常有用
- Add:向集合中插入元素
- TryTake:从集合中取出元素并删除
- TryPeek:从集合中取出元素,但不删除该元素
- BlockingCollection 与经典的阻塞队列数据结构类似
- Add :向容器中插入元素
- TryTake:从容器中取出元素并删除
- TryPeek:从容器中取出元素,但不删除。
- CompleteAdding:告诉容器,添加元素完成。此时如果还想继续添加会发生异常
- IsCompleted:告诉消费线程,生产者线程还在继续运行中,任务还未完成