本节主要内容:
** 简单的混合模式同步构造 SimpleHybridLock **
** 简单的混合模式同步构造自旋 AnotherHybridLock **
** 混合模式同步构造,ManualResetEventSlim **
** 混合模式同步构造,SemaphoreSlim **
** BlockingCollection **
(5)简单的混合模式同步构造
自己实现 SimpleHybridLock
class SimpleHybridLock : IDisposable
{
private int userLock = 0;
private AutoResetEvent kernelLock = new AutoResetEvent(false);
public void Enter()
{
if (Interlocked.Increment(ref userLock) == 1) { return; }
kernelLock.WaitOne();
}
public void Leave()
{
if (Interlocked.Decrement(ref userLock) == 0) { return; }
kernelLock.Set();
}
public void Dispose()
{
kernelLock.Dispose();
}
}
// 测试
SimpleHybridLock myLock = new SimpleHybridLock();
int result = 0;
int taskCount = 1000;
List<Task> taskList = new List<Task>();
for (int i = 0; i < taskCount; i++)
{
Task task = Task.Factory.StartNew(() =>
{
for (int j = 0; j < 1000; j++)
{
myLock.Enter();
result = result + 1;
myLock.Leave();
}
});
taskList.Add(task);
}
Task.WaitAll(taskList.ToArray());
Console.WriteLine(result);
(6)简单的混合模式同步构造自旋
自己实现 AnotherHybridLock
class AnotherHybridLock : IDisposable
{
private int userLock = 0, ownThreadId = 0, ownCount = 0, spinCount = 4000;
private AutoResetEvent kernelLock = new AutoResetEvent(false);
public void Enter()
{
int threadId = Thread.CurrentThread.ManagedThreadId;
if (threadId == ownThreadId)
{
ownCount = ownCount + 1;
return;
}
SpinWait spinWait = new SpinWait();
for (int i = 0; i < spinCount; i++)
{
if (Interlocked.CompareExchange(ref userLock, 1, 0) == 0) { goto GotLock; }
spinWait.SpinOnce();
}
if (Interlocked.Increment(ref userLock) > 1)
{
kernelLock.WaitOne();
}
GotLock:
ownThreadId = threadId;
ownCount = 1;
}
public void Leave()
{
int threadId = Thread.CurrentThread.ManagedThreadId;
if (threadId != ownThreadId)
{
throw new SynchronizationLockException("Lock not owned by calling thread");
}
ownCount = ownCount - 1;
if (ownCount > 0)
{
return;
}
ownThreadId = 0;
if (Interlocked.Decrement(ref userLock) == 0)
{
return;
}
kernelLock.Set();
}
public void Dispose() { kernelLock.Dispose(); }
}
// 测试
AnotherHybridLock myLock = new AnotherHybridLock();
int result = 0;
int taskCount = 1000;
List<Task> taskList = new List<Task>();
for (int i = 0; i < taskCount; i++)
{
Task task = Task.Factory.StartNew(() =>
{
for (int j = 0; j < 1000; j++)
{
myLock.Enter();
result = result + 1;
myLock.Leave();
}
});
taskList.Add(task);
}
Task.WaitAll(taskList.ToArray());
Console.WriteLine(result);
(7)FCL自带的混合模式同步构造 ManualResetEventSlim
FCL: Framework Class Library
ManualResetEventSlim【System.Threading.ManualResetEventSlim】
(1)有信号 (2)无信号
根据“信号意图”,选择适合的方案
# 线程之间传递信号,是为了传递数据,适用队列,生产消费模式
# 线程之间传递信号,是为了竞争共享数据,需要使用锁模式
# 其他信号类型:ManualResetEventSlim,AutoResetEvent, CountdownEvent, or Barrier
private static readonly ManualResetEventSlim signal = new ManualResetEventSlim();
static void Main(string[] args)
{
Task.Factory.StartNew(() =>
{
Thread.Sleep(10 * 1000);
signal.Set(); // 设置 signal
});
Task.Factory.StartNew(() =>
{
signal.Wait(); // 给老子等到起,我先在用户模式自旋打转等待 ....
// 等了半天,signal 还没来,为了不浪费 CPU 时间
// 我转入内核模式等待 ....
Console.WriteLine("hi");
});
Console.ReadLine();
}
(6)FCL自带的混合模式同步构造 SemaphoreSlim
(1)用来限制,并发访问共享资源的线程数量
(2)混合模式构造,就是为了尽可能减少,线程上下文切换
// 并发数量为3
private static readonly SemaphoreSlim signal = new SemaphoreSlim(3);
static void Main(string[] args)
{
for (int i = 0; i < 10; i++) // 共10个线程
{
Task.Factory.StartNew(() =>
{
signal.Wait(); // 3个线程不用等待
// 其他线程首先在用户模式自旋打转等待 ....
// 等待太久,则自动进入内核模式等待 ....
Console.WriteLine("hi");
Thread.Sleep(5 * 1000);
signal.Release(); // 释放信号
});
}
Console.ReadLine();
}
BlockingCollection【System.Collections.Concurrent.BlockingCollection<T>】
生产线程,消费线程
在消费这个集合的时候,是等待阻塞的
如果需要在消费这个集合的时候,不用等待呢?
简单的例子,如果 UI 是消费线程, UI 线程不可能通过等待的方式去处理队列里的项
详见,异步队列
初见 TPL Dataflow library
private static readonly BlockingCollection<int> blockQueue = new BlockingCollection<int>();
Task.Factory.StartNew(() =>
{
int num = 10;
while (num > 0)
{
blockQueue.Add(num);
num = num - 1;
Thread.Sleep(1 * 1000);
}
//blockQueue.CompleteAdding(); // 标志完成
});
//blockQueue.Add(3); // 标志完成,不能再添加
//blockQueue.Take(); // 消费1个
foreach (int num in blockQueue.GetConsumingEnumerable()) // 集合未标志完成,则会阻塞
{
Console.WriteLine(num);
Thread.Sleep(2 * 1000);
}
Console.WriteLine(blockQueue.Count); // 消费后,就移除
Console.ReadLine();








网友评论