图片 5

一同机制,限量使用

主题材料抽象:当某一能源均等时刻同意毫无疑问数额的线程使用的时候,须求有个机制来阻塞多余的线程,直到能源再次变得可用。
线程同步方案:Semaphore、SemaphoreSlim、CountdownEvent
方案特性:限量供应;除全部者外,别的人无条件等待;先到先得,尚未先后顺序

当两个职责或线程并行运维时,难以免止的对一些有限的财富开始展览并发的拜访。能够设想动用随机信号量来开始展览那上头的主宰(System.Threading.Semaphore)是代表三个Windows内核的时限信号量对象。假设预测等待的年华很短,能够设想使用SemaphoreSlim,它则带来的开支越来越小。.NetFrameWork中的连续信号量通过追踪进入和距离的职责或线程来和睦对财富的拜会。时域信号量供给精晓能源的最大数量,当二个职责进入时,能源计数器会被减壹,当计数器为0时,假使有职务访问财富,它会被封堵,直到有任务离开停止。
假定供给有跨进程或AppDomain的同步时,能够思索接纳塞马phore。Semaphore是获得的Windows
内核的数字信号量,所以在漫天系统中是实惠的。它根本的接口是Release和WaitOne,使用的办法和SemaphoreSlim是如出壹辙的。
时域信号量Semaphore是其余3个CLEnclave中的内核同步对象。在.net中,类Semaphore封装了那一个目标。与专门的学业的排他锁对象(Monitor,Mutex,SpinLock)分化的是,它不是叁个排他的锁对象,它与SemaphoreSlim,ReaderWriteLock等壹律允许多个少于的线程同时访问共享内部存储器能源。

   

1、Semaphore类
     
用于调控线程的访问数量,私下认可的构造函数为initialCount和maximumCount,表示暗许设置的确定性信号量个数和最大时限信号量个数。当你WaitOne的时候,功率信号量自减,当Release的时候,非非确定性信号量自增,然则当时限信号量为0的时候,后续的线程就不可能获得WaitOne了,所以必须等待先前的线程通过Release来释放。

Semaphore就象是1个栅栏,有一定的容积,当在那之中的线程数量到达安装的最大值时候,就不曾线程能够进来。然后,如若3个线程职业成就今后出来了,那下多少个线程就足以进入了。Semaphore的WaitOne或Release等操作分别将活动地递减只怕递增随机信号量的此时此刻计数值。当线程试图对计数值已经为0的时限信号量推行WaitOne操作时,线程将阻塞直到计数值大于0。在构造Semaphore时,最少需求一个参数。实信号量的发端体量和最大的容积。

   
 承接上1篇,大家连续说下.net四.0中的同步机制,是的,当出现了并行计算的时候,轻量等级的一块机制应时而生,在频限信号量那1块

图片 1图片 2

Semaphore的WaitOne恐怕Release方法的调用大致会开支1阿秒的系统时间,而优化后的SemaphoreSlim则供给大致四分壹皮秒。在测算中山学院量屡屡使用它的时候SemaphoreSlim依旧优势显然,加上SemaphoreSlim还助长了数不胜数接口,越发惠及大家实行支配,所以在肆.0自此的三十二线程开拓中,推荐应用SemaphoreSlim。塞马phoreSlim的落成如下:

并发了一多元的轻量级,前几日持续介绍下边的二个信号量 Countdown伊芙nt,SemaphoreSlim,马努alReset伊夫ntSlim。

using System;
using System.Threading;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Thread t1 = new Thread(Run1);
            t1.Start();
            Thread t2 = new Thread(Run2);
            t2.Start();
            Thread t3 = new Thread(Run3);
            t3.Start();
            Console.ReadKey();
        }

        //初始可以授予2个线程信号,因为第3个要等待前面的Release才能得到信号
        static Semaphore sem = new Semaphore(2, 10);

        static void Run1()
        {
            sem.WaitOne();
            Console.WriteLine("大家好,我是Run1;" + DateTime.Now.ToString("mm:ss"));

            //两秒后
            Thread.Sleep(2000);
            sem.Release();
        }

        static void Run2()
        {
            sem.WaitOne();
            Console.WriteLine("大家好,我是Run2;" + DateTime.Now.ToString("mm:ss"));

            //两秒后
            Thread.Sleep(2000);
            sem.Release();
        }

        static void Run3()
        {
            sem.WaitOne();
            Console.WriteLine("大家好,我是Run3;" + DateTime.Now.ToString("mm:ss"));

            //两秒后
            Thread.Sleep(2000);
            sem.Release();
        }
    }
}
public class SemaphoreSlim : IDisposable
    {  
        private volatile int m_currentCount; //可用数的资源数,<=0开始阻塞
        private readonly int m_maxCount;
        private volatile int m_waitCount; //阻塞的线程数
        private object m_lockObj;
        private volatile ManualResetEvent m_waitHandle;
        private const int NO_MAXIMUM = Int32.MaxValue;
        //Head of list representing asynchronous waits on the semaphore.
        private TaskNode m_asyncHead;
        // Tail of list representing asynchronous waits on the semaphore.
        private TaskNode m_asyncTail;
         // A pre-completed task with Result==true
        private readonly static Task<bool> s_trueTask =
            new Task<bool>(false, true, (TaskCreationOptions)InternalTaskOptions.DoNotDispose, default(CancellationToken));

        public SemaphoreSlim(int initialCount) : this(initialCount, NO_MAXIMUM){ }        
        public SemaphoreSlim(int initialCount, int maxCount)
        {
            if (initialCount < 0 || initialCount > maxCount)
            {
                throw new ArgumentOutOfRangeException("initialCount", initialCount, GetResourceString("SemaphoreSlim_ctor_InitialCountWrong"));
            }
            if (maxCount <= 0)
            {
                throw new ArgumentOutOfRangeException("maxCount", maxCount, GetResourceString("SemaphoreSlim_ctor_MaxCountWrong"));
            }
            m_maxCount = maxCount;
            m_lockObj = new object();
            m_currentCount = initialCount;
        }
        public void Wait(){Wait(Timeout.Infinite, new CancellationToken());}
        public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken)
        {
            CheckDispose();
            if (millisecondsTimeout < -1)
            {
                throw new ArgumentOutOfRangeException("totalMilliSeconds", millisecondsTimeout, GetResourceString("SemaphoreSlim_Wait_TimeoutWrong"));
            }
            cancellationToken.ThrowIfCancellationRequested();
            uint startTime = 0;
            if (millisecondsTimeout != Timeout.Infinite && millisecondsTimeout > 0)
            {
                startTime = TimeoutHelper.GetTime();
            }

            bool waitSuccessful = false;
            Task<bool> asyncWaitTask = null;
            bool lockTaken = false;

            CancellationTokenRegistration cancellationTokenRegistration = cancellationToken.InternalRegisterWithoutEC(s_cancellationTokenCanceledEventHandler, this);
            try
            {
                SpinWait spin = new SpinWait();
                while (m_currentCount == 0 && !spin.NextSpinWillYield)
                {
                    spin.SpinOnce();
                }
                try { }
                finally
                {
                    Monitor.Enter(m_lockObj, ref lockTaken);
                    if (lockTaken)
                    {
                        m_waitCount++;
                    }
                }

                // If there are any async waiters, for fairness we'll get in line behind
                if (m_asyncHead != null)
                {
                    Contract.Assert(m_asyncTail != null, "tail should not be null if head isn't");
                    asyncWaitTask = WaitAsync(millisecondsTimeout, cancellationToken);
                }
                // There are no async waiters, so we can proceed with normal synchronous waiting.
                else
                {
                    // If the count > 0 we are good to move on.
                    // If not, then wait if we were given allowed some wait duration
                    OperationCanceledException oce = null;
                    if (m_currentCount == 0)
                    {
                        if (millisecondsTimeout == 0)
                        {
                            return false;
                        }
                        // Prepare for the main wait...
                        // wait until the count become greater than zero or the timeout is expired
                        try
                        {
                            waitSuccessful = WaitUntilCountOrTimeout(millisecondsTimeout, startTime, cancellationToken);
                        }
                        catch (OperationCanceledException e) { oce = e; }
                    }

                    Contract.Assert(!waitSuccessful || m_currentCount > 0, "If the wait was successful, there should be count available.");
                    if (m_currentCount > 0)
                    {
                        waitSuccessful = true;
                        m_currentCount--;
                    }
                    else if (oce != null)
                    {
                        throw oce;
                    }
                    if (m_waitHandle != null && m_currentCount == 0)
                    {
                        m_waitHandle.Reset();
                    }
                }
            }
            finally
            {
                // Release the lock
                if (lockTaken)
                {
                    m_waitCount--;
                    Monitor.Exit(m_lockObj);
                }

                // Unregister the cancellation callback.
                cancellationTokenRegistration.Dispose();
            }
            return (asyncWaitTask != null) ? asyncWaitTask.GetAwaiter().GetResult() : waitSuccessful;
        }

        private bool WaitUntilCountOrTimeout(int millisecondsTimeout, uint startTime, CancellationToken cancellationToken)
        {
            int remainingWaitMilliseconds = Timeout.Infinite;
            //Wait on the monitor as long as the count is zero
            while (m_currentCount == 0)
            {
                // If cancelled, we throw. Trying to wait could lead to deadlock.
                cancellationToken.ThrowIfCancellationRequested();
                if (millisecondsTimeout != Timeout.Infinite)
                {
                    remainingWaitMilliseconds = TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout);
                    if (remainingWaitMilliseconds <= 0)
                    {
                        // The thread has expires its timeout
                        return false;
                    }
                }
                // ** the actual wait **
                if (!Monitor.Wait(m_lockObj, remainingWaitMilliseconds))
                {
                    return false;
                }
            }
            return true;
        }
        public Task<bool> WaitAsync(int millisecondsTimeout, CancellationToken cancellationToken)
        {
            CheckDispose();
            // Validate input
            if (millisecondsTimeout < -1)
            {
                throw new ArgumentOutOfRangeException("totalMilliSeconds", millisecondsTimeout, GetResourceString("SemaphoreSlim_Wait_TimeoutWrong"));
            }
            // Bail early for cancellation
            if (cancellationToken.IsCancellationRequested)
                return Task.FromCancellation<bool>(cancellationToken);

            lock (m_lockObj)
            {
                // If there are counts available, allow this waiter to succeed.
                if (m_currentCount > 0)
                {
                    --m_currentCount;
                    if (m_waitHandle != null && m_currentCount == 0) m_waitHandle.Reset();
                    return s_trueTask;
                }
                    // If there aren't, create and return a task to the caller.
                    // The task will be completed either when they've successfully acquired
                    // the semaphore or when the timeout expired or cancellation was requested.
                else
                {
                    Contract.Assert(m_currentCount == 0, "m_currentCount should never be negative");
                    var asyncWaiter = CreateAndAddAsyncWaiter();
                    return (millisecondsTimeout == Timeout.Infinite && !cancellationToken.CanBeCanceled) ?
                        asyncWaiter :
                        WaitUntilCountOrTimeoutAsync(asyncWaiter, millisecondsTimeout, cancellationToken);
                }
            }
        }

        /// <summary>Creates a new task and stores it into the async waiters list.</summary>
        /// <returns>The created task.</returns>
        private TaskNode CreateAndAddAsyncWaiter()
        {
            Contract.Assert(Monitor.IsEntered(m_lockObj), "Requires the lock be held");
            // Create the task
            var task = new TaskNode();
            // Add it to the linked list
            if (m_asyncHead == null)
            {
                Contract.Assert(m_asyncTail == null, "If head is null, so too should be tail");
                m_asyncHead = task;
                m_asyncTail = task;
            }
            else
            {
                Contract.Assert(m_asyncTail != null, "If head is not null, neither should be tail");
                m_asyncTail.Next = task;
                task.Prev = m_asyncTail;
                m_asyncTail = task;
            }
            // Hand it back
            return task;
        }

        private async Task<bool> WaitUntilCountOrTimeoutAsync(TaskNode asyncWaiter, int millisecondsTimeout, CancellationToken cancellationToken)
        {
            Contract.Assert(asyncWaiter != null, "Waiter should have been constructed");
            Contract.Assert(Monitor.IsEntered(m_lockObj), "Requires the lock be held");
            using (var cts = cancellationToken.CanBeCanceled ?
                CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, default(CancellationToken)) :
                new CancellationTokenSource())
            {
                var waitCompleted = Task.WhenAny(asyncWaiter, Task.Delay(millisecondsTimeout, cts.Token));
                if (asyncWaiter == await waitCompleted.ConfigureAwait(false))
                {
                    cts.Cancel(); // ensure that the Task.Delay task is cleaned up
                    return true; // successfully acquired
                }
            }

            // If we get here, the wait has timed out or been canceled.

            // If the await completed synchronously, we still hold the lock.  If it didn't,
            // we no longer hold the lock.  As such, acquire it.
            lock (m_lockObj)
            {
                // Remove the task from the list.  If we're successful in doing so,
                // we know that no one else has tried to complete this waiter yet,
                // so we can safely cancel or timeout.
                if (RemoveAsyncWaiter(asyncWaiter))
                {
                    cancellationToken.ThrowIfCancellationRequested(); // cancellation occurred
                    return false; // timeout occurred
                }
            }

            // The waiter had already been removed, which means it's already completed or is about to
            // complete, so let it, and don't return until it does.
            return await asyncWaiter.ConfigureAwait(false) await asyncWaiter.ConfigureAwait(false);
        }
        public int Release(){ return Release(1);}

        public int Release(int releaseCount)
        {
            CheckDispose();

            // Validate input
            if (releaseCount < 1)
            {
                throw new ArgumentOutOfRangeException( "releaseCount", releaseCount, GetResourceString("SemaphoreSlim_Release_CountWrong"));
            }
            int returnCount;

            lock (m_lockObj)
            {
                // Read the m_currentCount into a local variable to avoid unnecessary volatile accesses inside the lock.
                int currentCount = m_currentCount;
                returnCount = currentCount;

                // If the release count would result exceeding the maximum count, throw SemaphoreFullException.
                if (m_maxCount - currentCount < releaseCount)
                {
                    throw new SemaphoreFullException();
                }

                // Increment the count by the actual release count
                currentCount += releaseCount;

                // Signal to any synchronous waiters
                int waitCount = m_waitCount;
                if (currentCount == 1 || waitCount == 1)
                {
                    Monitor.Pulse(m_lockObj);
                }
                else if (waitCount > 1)
                {
                    Monitor.PulseAll(m_lockObj);
                }

                // Now signal to any asynchronous waiters, if there are any.  While we've already
                // signaled the synchronous waiters, we still hold the lock, and thus
                // they won't have had an opportunity to acquire this yet.  So, when releasing
                // asynchronous waiters, we assume that all synchronous waiters will eventually
                // acquire the semaphore.  That could be a faulty assumption if those synchronous
                // waits are canceled, but the wait code path will handle that.
                if (m_asyncHead != null)
                {
                    Contract.Assert(m_asyncTail != null, "tail should not be null if head isn't null");
                    int maxAsyncToRelease = currentCount - waitCount;
                    while (maxAsyncToRelease > 0 && m_asyncHead != null)
                    {
                        --currentCount;
                        --maxAsyncToRelease;

                        // Get the next async waiter to release and queue it to be completed
                        var waiterTask = m_asyncHead;
                        RemoveAsyncWaiter(waiterTask); // ensures waiterTask.Next/Prev are null
                        QueueWaiterTask(waiterTask);
                    }
                }
                m_currentCount = currentCount;

                // Exposing wait handle if it is not null
                if (m_waitHandle != null && returnCount == 0 && currentCount > 0)
                {
                    m_waitHandle.Set();
                }
            }

            // And return the count
            return returnCount;
        }

        ///Removes the waiter task from the linked list.</summary>
        private bool RemoveAsyncWaiter(TaskNode task)
        {
            Contract.Requires(task != null, "Expected non-null task");
            Contract.Assert(Monitor.IsEntered(m_lockObj), "Requires the lock be held");

            // Is the task in the list?  To be in the list, either it's the head or it has a predecessor that's in the list.
            bool wasInList = m_asyncHead == task || task.Prev != null;

            // Remove it from the linked list
            if (task.Next != null) task.Next.Prev = task.Prev;
            if (task.Prev != null) task.Prev.Next = task.Next;
            if (m_asyncHead == task) m_asyncHead = task.Next;
            if (m_asyncTail == task) m_asyncTail = task.Prev;
            Contract.Assert((m_asyncHead == null) == (m_asyncTail == null), "Head is null iff tail is null");

            // Make sure not to leak
            task.Next = task.Prev = null;

            // Return whether the task was in the list
            return wasInList;
        }
        private static void QueueWaiterTask(TaskNode waiterTask)
        {
            ThreadPool.UnsafeQueueCustomWorkItem(waiterTask, forceGlobal: false);
        }
        public int CurrentCount
        {
            get { return m_currentCount; }
        }
        public WaitHandle AvailableWaitHandle
        {
            get
            {
                CheckDispose();
                if (m_waitHandle != null)
                    return m_waitHandle;
                lock (m_lockObj)
                {
                    if (m_waitHandle == null)
                    {
                        m_waitHandle = new ManualResetEvent(m_currentCount != 0);
                    }
                }
                return m_waitHandle;
            }
        }
        private sealed class TaskNode : Task<bool>, IThreadPoolWorkItem
        {
            internal TaskNode Prev, Next;
            internal TaskNode() : base() {}

            [SecurityCritical]
            void IThreadPoolWorkItem.ExecuteWorkItem()
            {
                bool setSuccessfully = TrySetResult(true);
                Contract.Assert(setSuccessfully, "Should have been able to complete task");
            }

            [SecurityCritical]
            void IThreadPoolWorkItem.MarkAborted(ThreadAbortException tae) { /* nop */ }
        }
    }

 

Program

SemaphoreSlim类有多少个私有字段很关键,m_currentCount表示可用财富,假诺m_currentCount>0老是调用Wait都会减1,当m_currentCount<=0时再一次调用Wait方法就能阻塞。每便调用Release方法m_currentCount都会加1.m_maxCount表示最大可用财富数,是在构造函数中钦定的。m_waitCount代表近年来不通的线程数。TaskNode
m_asyncHead,m_asyncTail那三个变量重要用以异步方法。

一:CountdownEvent

在上述的秘技中Release()方法相当于自增八个模拟信号量,Release(伍)自增陆个功率信号量。只是,Release()到构造函数的第3个参数maximumCount的值就不能再自增了。

我们率先来探视Wait方法,这里还有它的异步版本WaitAsync。在Wait方法中首先检查m_currentCount是还是不是为0,假使是大家用SpinWait自旋13次;放4一遍Wait都亟待锁住m_lockObj对象,m_asyncHead
!=
null表示近来已经存在异步的靶子,所以大家调用WaitAsync方法,假设未有那么大家调用WaitUntilCountOrTimeout方法,该办法在m_currentCount==0会阻塞到到m_currentCount不为0或然逾期;看到WaitUntilCountOr提姆eout方法中【if
(!Monitor.Wait(m_lockObj,
remainingWaitMilliseconds))】,就很明了Wait方法中【CancellationTokenRegistration
cancellationTokenRegistration =
cancellationToken.InternalRegisterWithoutEC(s_cancellationTokenCanceled伊夫ntHandler,
this)】存在的缘由了,确实很神奇【这里和马努alReset伊芙ntSlim相似】。今后我们回到WaitAsync方法,该情势也是率先检查m_currentCount是或不是大于0,大于直接重临。否者调用CreateAndAddAsyncWaiter创建几个Task<bool>【Task<bool>是一个链表结构】,倘若未有收回且超时超过-一,那么就调用WaitUntilCountOrTimeoutAsync方法,该方法首先包装3个Task【var
waitCompleted = Task.WhenAny(asyncWaiter,
Task.Delay(millisecondsTimeout, cts.Token))】然后等待线程【await
waitCompleted.ConfigureAwait(false)】重临的是asyncWaiter或许另三个Delay的Task。如若回到的不是asyncWaiter表明已经晚点须要调用RemoveAsyncWaiter,然后回到
await
asyncWaiter.ConfigureAwait(false),假使回到的是asyncWaiter,那么就调用Cancel方法。那么这里的asyncWaiter.ConfigureAwait(false)几时退出了【或然说不阻塞】,那就要看Release中的QueueWaiterTask方法了。

   
 那种使用实信号状态的同步基元分外适合在动态的fork,join的场馆,它使用“能量信号计数”的办法,就比如那样,二个麻将桌只好兼容陆个

Semaphore可用于进度级交互。

QueueWaiterTask方法或调用TaskNode的ExecuteWorkItem方法。
这将来大家来探望Release方法,该办法会把currentCount加壹,然后把等待线程转为就绪线程【Monitor.Pulse(m_lockObj)或
Monitor.PulseAll(m_lockObj)】,假使存在异步的话,看看还是能自由多少个异步task【
int maxAsyncToRelease = currentCount –
waitCount】,这里Release的讲明很关键,只是没怎么知道,现释同步的waiters,然后在出狱异步的waiters,可是自由同步后锁的财富未有自由,在放出异步的waiters时候是把currentCount减一,这样以为异步waiters优先获得能源。也不清楚自个儿的明亮是不是科学?
壹)当ConfigureAwait(true),代码由共同施行进入异步施行时,当前2只施行的线程上下文音讯(举个例子HttpConext.Current,Thread.CurrentThread.CurrentCulture)就能被捕获并保存至SynchronizationContext中,供异步实施中选用,并且供异步实施到位之后(await之后的代码)的联合实施中使用(即便await之后是手拉手推行的,可是发生了线程切换,会在此外一个线程中进行「ASP.NET场景」)。那么些捕获当然是有代价的,当时大家误认为品质难点是这些地点的支出引起,但实质上那几个费用异常的小,在我们的行使场景不至于会拉动品质难点。

人打麻将,即使后来的人也想搓1把碰碰运气,那么她必须等待直到麻将桌上的人走掉壹位。好,那正是轻巧的实信号计数机制,从技艺角

图片 3图片 4

2)当Configurewait(flase),则不开始展览线程上下文音讯的捕获,async方法中与await之后的代码实行时就不可能取得await在此之前的线程的上下文消息,在ASP.NET中最直接的震慑正是HttpConext.Current的值为null。

度上的话它是概念了最多能够进入重视代码的线程数。

using System;
using System.Diagnostics;
using System.Threading;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {

            Thread t1 = new Thread(Run1);
            t1.Start();

            Thread t2 = new Thread(Run2);
            t2.Start();

            Console.Read();
        }

        //初始可以授予2个线程信号,因为第3个要等待前面的Release才能得到信号
        static Semaphore sem = new Semaphore(3, 10, "命名Semaphore");

        static void Run1()
        {
            sem.WaitOne();

            Console.WriteLine("进程:" + Process.GetCurrentProcess().Id + "  我是Run1" + DateTime.Now.TimeOfDay);
        }

        static void Run2()
        {
            sem.WaitOne();

            Console.WriteLine("进程:" + Process.GetCurrentProcess().Id + "  我是Run2" + DateTime.Now.TimeOfDay);
        }
    }
}

   
 可是Countdown伊夫nt更牛X之处在于大家得以动态的更改“时域信号计数”的深浅,比方壹会儿能够容纳柒个线程,一下又四个,一下又十一个,

Program

这般做有怎样便宜吗?照旧承袭上1篇文章所说的,比方三个职责急需加载一w条数据,那么恐怕出现那种境况。

图片 5

 

直白运维两回bin目录的exe文件,就能够觉察最两只可以输出1个。

加载User表:         依照user表的数据量,大家须求开四个task。

Semaphore能够限制可同时做客某一财富或能源池的线程数。

加载Product表:    产品表数据相对相比多,总计之后须求开捌个task。

       
塞马phore类在里面维护1个计数器,当贰个线程调用Semaphore对象的Wait种类措施时,此计数器减1,只要计数器照旧三个正数,线程就不会卡住。当计数器减到0时,再调用Semaphore对象Wait体系措施的线程将被卡住,直到有线程调用Semaphore对象的Release()方法扩充计数器值时,才有十分大希望清除阻塞状态。

加载order表:       由于自家的网址订单增加,总结之后需求开13个task。

 

 

示范表达:
体育场面都安插有几多台公用Computer供读者查询音信,当某日读者相比多时,必须排队等待。UseLibraryComputer实例用二10十二线程模拟了三人采纳多台Computer的长河

原先的篇章也说了,大家供给和谐task在多阶段加载数据的壹块儿难题,那么如何回答这里的伍,八,12,幸而,Countdown伊夫nt给大家提供了

图片 6图片 7

能够动态修改的消除方案。

using System;
using System.Threading;

namespace ConsoleApp1
{
    class Program
    {
        //图书馆拥有的公用计算机  
        private const int ComputerNum = 3;
        private static Computer[] LibraryComputers;
        //同步信号量  
        public static Semaphore sp = new Semaphore(ComputerNum, ComputerNum);

        static void Main(string[] args)
        {
            //图书馆拥有ComputerNum台电脑  
            LibraryComputers = new Computer[ComputerNum];
            for (int i = 0; i < ComputerNum; i++)
                LibraryComputers[i] = new Computer("Computer" + (i + 1).ToString());
            int peopleNum = 0;
            Random ran = new Random();
            Thread user;
            System.Console.WriteLine("敲任意键模拟一批批的人排队使用{0}台计算机,ESC键结束模拟……", ComputerNum);
            //每次创建若干个线程,模拟人排队使用计算机  
            while (System.Console.ReadKey().Key != ConsoleKey.Escape)
            {
                peopleNum = ran.Next(0, 10);
                System.Console.WriteLine("\n有{0}人在等待使用计算机。", peopleNum);

                for (int i = 1; i <= peopleNum; i++)
                {
                    user = new Thread(UseComputer);
                    user.Start("User" + i.ToString());
                }
            }
        }

        //线程函数  
        static void UseComputer(Object UserName)
        {
            sp.WaitOne();//等待计算机可用  

            //查找可用的计算机  
            Computer cp = null;
            for (int i = 0; i < ComputerNum; i++)
                if (LibraryComputers[i].IsOccupied == false)
                {
                    cp = LibraryComputers[i];
                    break;
                }
            //使用计算机工作  
            cp.Use(UserName.ToString());

            //不再使用计算机,让出来给其他人使用  
            sp.Release();
        }
    }

    class Computer
    {
        public readonly string ComputerName = "";
        public Computer(string Name)
        {
            ComputerName = Name;
        }
        //是否被占用  
        public bool IsOccupied = false;
        //人在使用计算机  
        public void Use(String userName)
        {
            System.Console.WriteLine("{0}开始使用计算机{1}", userName, ComputerName);
            IsOccupied = true;
            Thread.Sleep(new Random().Next(1, 2000)); //随机休眠,以模拟人使用计算机  
            System.Console.WriteLine("{0}结束使用计算机{1}", userName, ComputerName);
            IsOccupied = false;
        }
    }
}

 

Program

  1 using System.Collections.Concurrent;
  2 using System.Threading.Tasks;
  3 using System;
  4 using System.Diagnostics;
  5 using System.Collections.Generic;
  6 using System.Linq;
  7 using System.Threading;
  8 
  9 class Program
 10 {
 11     //默认的容纳大小为“硬件线程“数
 12     static CountdownEvent cde = new CountdownEvent(Environment.ProcessorCount);
 13 
 14     static void Main(string[] args)
 15     {
 16         //加载User表需要5个任务
 17         var userTaskCount = 5;
 18 
 19         //重置信号
 20         cde.Reset(userTaskCount);
 21 
 22         for (int i = 0; i < userTaskCount; i++)
 23         {
 24             Task.Factory.StartNew((obj) =>
 25             {
 26                 LoadUser(obj);
 27             }, i);
 28         }
 29 
 30         //等待所有任务执行完毕
 31         cde.Wait();
 32 
 33         Console.WriteLine("\nUser表数据全部加载完毕!\n");
 34 
 35         //加载product需要8个任务
 36         var productTaskCount = 8;
 37 
 38         //重置信号
 39         cde.Reset(productTaskCount);
 40 
 41         for (int i = 0; i < productTaskCount; i++)
 42         {
 43             Task.Factory.StartNew((obj) =>
 44             {
 45                 LoadProduct(obj);
 46             }, i);
 47         }
 48 
 49         cde.Wait();
 50 
 51         Console.WriteLine("\nProduct表数据全部加载完毕!\n");
 52 
 53         //加载order需要12个任务
 54         var orderTaskCount = 12;
 55 
 56         //重置信号
 57         cde.Reset(orderTaskCount);
 58 
 59         for (int i = 0; i < orderTaskCount; i++)
 60         {
 61             Task.Factory.StartNew((obj) =>
 62             {
 63                 LoadOrder(obj);
 64             }, i);
 65         }
 66 
 67         cde.Wait();
 68 
 69         Console.WriteLine("\nOrder表数据全部加载完毕!\n");
 70 
 71         Console.WriteLine("\n(*^__^*) 嘻嘻,恭喜你,数据全部加载完毕\n");
 72 
 73         Console.Read();
 74     }
 75 
 76     static void LoadUser(object obj)
 77     {
 78         try
 79         {
 80             Console.WriteLine("当前任务:{0}正在加载User部分数据!", obj);
 81         }
 82         finally
 83         {
 84             cde.Signal();
 85         }
 86     }
 87 
 88     static void LoadProduct(object obj)
 89     {
 90         try
 91         {
 92             Console.WriteLine("当前任务:{0}正在加载Product部分数据!", obj);
 93         }
 94         finally
 95         {
 96             cde.Signal();
 97         }
 98     }
 99 
100     static void LoadOrder(object obj)
101     {
102         try
103         {
104             Console.WriteLine("当前任务:{0}正在加载Order部分数据!", obj);
105         }
106         finally
107         {
108             cde.Signal();
109         }
110     }
111 }

 

 

2、SemaphoreSlim类

图片 8

     在.net
肆.0事先,framework中有二个重量级的Semaphore,能够跨进度同步,SemaphoreSlim轻量级不行,msdn对它的解释为:限制可同时做客某一能源或财富池的线程数。

咱俩看到有三个根本方法:Wait和Signal。每调用三遍Signal也就是麻将桌上走了一人,直到全部人都搓过麻将wait才给放行,这里同样要

图片 9图片 10

专注也正是“超时“难题的存在性,越发是在并行总括中,轻量等级给咱们提供了”撤废标志“的体制,那是在重量等级中不设有的,比如下边的

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static SemaphoreSlim slim = new SemaphoreSlim(Environment.ProcessorCount, 12);

        static void Main(string[] args)
        {
            for (int i = 0; i < 12; i++)
            {
                Task.Factory.StartNew((obj) =>
                {
                    Run(obj);
                }, i);
            }
            Console.Read();
        }

        static void Run(object obj)
        {
            slim.Wait();
            Console.WriteLine("当前时间:{0}任务 {1}已经进入。", DateTime.Now, obj);
            //这里busy3s中
            Thread.Sleep(3000);
            slim.Release();
        }
    }
}

重载public bool Wait(int millisecondsTimeout, CancellationToken
cancellationToken),具体运用能够看前1篇作品的牵线。

Program

 

一致,制止死锁的情景,大家必要明白”超时和收回标识“的减轻方案,像SemaphoreSlim那种定死的”线程请求范围“,其实是降低了扩张性,使用需谨慎,在以为有至关重要的时候利用它

二:SemaphoreSlim

注:Semaphore类是SemaphoreSlim类的老版本,该版本采取纯粹的基础时间(kernel-time)格局。

     在.net
四.0此前,framework中有三个重量级的Semaphore,人家能够跨进度同步,咋轻量级不行,msdn对它的解释为:限制可同时做客

    SemaphoreSlim类不应用Windows内核数字信号量,而且也不支持进度间壹块。所以在跨程序同步的场景下能够应用塞马phore

某一财富或财富池的线程数。关于它的分量级demo,作者的上一个名目多数有示范,你也足以精通为Countdown伊夫nt是
SemaphoreSlim的机能加

 

强版,好了,举多个轻量级使用的事例。

3、CountdownEvent类

 1 using System.Collections.Concurrent;
 2 using System.Threading.Tasks;
 3 using System;
 4 using System.Diagnostics;
 5 using System.Collections.Generic;
 6 using System.Linq;
 7 using System.Threading;
 8 
 9 class Program
10 {
11     static SemaphoreSlim slim = new SemaphoreSlim(Environment.ProcessorCount, 12);
12 
13     static void Main(string[] args)
14     {
15         for (int i = 0; i < 12; i++)
16         {
17             Task.Factory.StartNew((obj) =>
18             {
19                 Run(obj);
20             }, i);
21         }
22 
23         Console.Read();
24     }
25 
26     static void Run(object obj)
27     {
28         slim.Wait();
29 
30         Console.WriteLine("当前时间:{0}任务 {1}已经进入。", DateTime.Now, obj);
31 
32         //这里busy3s中
33         Thread.Sleep(3000);
34 
35         slim.Release();
36     }
37 }

   
 那种使用能量信号状态的联合具名基元相当适合在动态的fork,join的场合,它选用“实信号计数”的办法,就比如那样,八个麻将桌只可以兼容二人打麻将,如若后来的人也想搓1把碰碰运气,那么她必须等待直到麻将桌上的人走掉一人。好,那就是简单的功率信号计数机制,从本领角度上来说它是概念了最多能够进加入关贸总协定协会键代码的线程数。

 

   
 但是CountdownEvent更牛X之处在于大家得以动态的改造“随机信号计数”的分寸,举例1会儿能力所能达到容纳8个线程,一下又四个,一下又13个,那样做有怎么着好处吗?比方一个职务急需加载一w条数据,那么大概出现那种情景。

图片 11

例如:

同一,制止死锁的情形,我们要求通晓”超时和注销标识“的减轻方案,像SemaphoreSlim那种定死的”线程请求范围“,其实是降低了增加性,

加载User表:         依据user表的数据量,大家要求开多少个task。

所以说,试水有风险,使用需谨慎,在认为有不可或缺的时候利用它。

加载Product表:    产品表数据绝对相比较多,总结之后需求开九个task。

 

加载order表:       由于自己的网址订单增加,总计之后要求开10个task。

三: ManualResetEventSlim

图片 12图片 13

   
 相信它的份量等第大家都理解是马努alReset,而那一个轻量等第选用的是”自旋等待“+”内核等待“,相当于说先利用”自旋等待的章程“等待,

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        //默认的容纳大小为“硬件线程“数
        static CountdownEvent cde = new CountdownEvent(Environment.ProcessorCount);

        static void LoadUser(object obj)
        {
            try
            {
                Console.WriteLine("ThreadId={0};当前任务:{1}正在加载User部分数据!", Thread.CurrentThread.ManagedThreadId, obj);
            }
            finally
            {
                cde.Signal();
            }
        }

        static void LoadProduct(object obj)
        {
            try
            {
                Console.WriteLine("ThreadId={0};当前任务:{1}正在加载Product部分数据!", Thread.CurrentThread.ManagedThreadId, obj);
            }
            finally
            {
                cde.Signal();
            }
        }

        static void LoadOrder(object obj)
        {
            try
            {
                Console.WriteLine("ThreadId={0};当前任务:{1}正在加载Order部分数据!", Thread.CurrentThread.ManagedThreadId, obj);
            }
            finally
            {
                cde.Signal();
            }
        }

        static void Main(string[] args)
        {
            //加载User表需要5个任务
            var userTaskCount = 5;
            //重置信号
            cde.Reset(userTaskCount);
            for (int i = 0; i < userTaskCount; i++)
            {
                Task.Factory.StartNew((obj) =>
                {
                    LoadUser(obj);
                }, i);
            }
            //等待所有任务执行完毕
            cde.Wait();
            Console.WriteLine("\nUser表数据全部加载完毕!\n");

            //加载product需要8个任务
            var productTaskCount = 8;
            //重置信号
            cde.Reset(productTaskCount);
            for (int i = 0; i < productTaskCount; i++)
            {
                Task.Factory.StartNew((obj) =>
                {
                    LoadProduct(obj);
                }, i);
            }
            cde.Wait();
            Console.WriteLine("\nProduct表数据全部加载完毕!\n");

            //加载order需要12个任务
            var orderTaskCount = 12;
            //重置信号
            cde.Reset(orderTaskCount);
            for (int i = 0; i < orderTaskCount; i++)
            {
                Task.Factory.StartNew((obj) =>
                {
                    LoadOrder(obj);
                }, i);
            }
            cde.Wait();
            Console.WriteLine("\nOrder表数据全部加载完毕!\n");

            Console.WriteLine("\n(*^__^*) 嘻嘻,恭喜你,数据全部加载完毕\n");
            Console.Read();
        }
    }
}

直至另多少个职务调用set方法来刑满释放它。假设迟迟等不到自由,那么职务就能够进入基于内核的等候,所以说借使咱们掌握等待的时日相当短,采

Program

用轻量级的本子会怀有更好的质量,原理差不离就好像此,下面举个小例子。

大家看出有五个主要情势:Wait和Signal。每调用1回Signal相当于麻将桌上走了1个人,直到全部人都搓过麻将wait才给放行,这里同样要留心也正是“超时“难题的存在性,尤其是在并行总计中,轻量等第给我们提供了”打消标识“的建制,那是在重量等级中不存在的

 1 using System.Collections.Concurrent;
 2 using System.Threading.Tasks;
 3 using System;
 4 using System.Diagnostics;
 5 using System.Collections.Generic;
 6 using System.Linq;
 7 using System.Threading;
 8 
 9 class Program
10 {
11     //2047:自旋的次数
12     static ManualResetEventSlim mrs = new ManualResetEventSlim(false, 2047);
13 
14     static void Main(string[] args)
15     {
16 
17         for (int i = 0; i < 12; i++)
18         {
19             Task.Factory.StartNew((obj) =>
20             {
21                 Run(obj);
22             }, i);
23         }
24 
25         Console.WriteLine("当前时间:{0}我是主线程{1},你们这些任务都等2s执行吧:\n",
26         DateTime.Now,
27         Thread.CurrentThread.ManagedThreadId);
28         Thread.Sleep(2000);
29 
30         mrs.Set();
31 
32         Console.Read();
33     }
34 
35     static void Run(object obj)
36     {
37         mrs.Wait();
38 
39         Console.WriteLine("当前时间:{0}任务 {1}已经进入。", DateTime.Now, obj);
40     }
41 }

注:倘若调用Signal()未有达到内定的次数,那么Wait()将直接等候,请保管使用各种线程完结后都要调用Signal方法。

 

 

图片 14