.NET多线程(九)并发同步

本节主要内容:

** 简单的混合模式同步构造 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 taskList = new List();
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 taskList = new List();
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
生产线程,消费线程

在消费这个集合的时候,是等待阻塞的
如果需要在消费这个集合的时候,不用等待呢?
简单的例子,如果 UI 是消费线程, UI 线程不可能通过等待的方式去处理队列里的项
详见,异步队列

初见 TPL Dataflow library


private static readonly BlockingCollection blockQueue = new BlockingCollection();

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

你可能感兴趣的:(.NET多线程(九)并发同步)