C# 并行编程 之 限制资源的并发访问 使用SemaphoreSlim

来源:互联网 发布:天猫魔盒看电视剧软件 编辑:程序博客网 时间:2024/06/18 14:49

概要

当多个任务或线程并行运行时,难以避免的对某些有限的资源进行并发的访问。可以考虑使用信号量来进行这方面的控制(System.Threading.Semaphore)是表示一个Windows内核的信号量对象。如果预计等待的时间较短,可以考虑使用SemaphoreSlim,它则带来的开销更小。

.NetFrameWork中的信号量通过跟踪进入和离开的任务或线程来协调对资源的访问。信号量需要知道资源的最大数量,当一个任务进入时,资源计数器会被减1,当计数器为0时,如果有任务访问资源,它会被阻塞,直到有任务离开为止。

示例程序: 10个任务并行访问3个资源

using System;using System.Text;using System.Threading;using System.Threading.Tasks;using System.Diagnostics;namespace Sample5_8_semaphoreslim{    class Program    {        private static int _TaskNum = 10;        private static Task[] _Tasks;        private const int MAX_RESOURCE = 3;        private const int RUN_LOOP = 10;        private static SemaphoreSlim m_Semaphore;        private static void Work1(int TaskID)        {            int i = 0;            var sw = Stopwatch.StartNew();            var rnd = new Random();            while (i < RUN_LOOP)            {                Thread.Sleep(rnd.Next(200, 500));                Console.WriteLine("TASK " + TaskID + " REQUESTing {");                m_Semaphore.Wait();                try                {                    Console.WriteLine("TASK " + TaskID + " WOrking  ...  ..." + i);                    sw.Restart();                    Thread.Sleep(rnd.Next(200, 500));                }                finally                {                    Console.WriteLine("TASK " + TaskID + " REQUESTing }");                    m_Semaphore.Release();                    i++;                }            }        }        static void Main(string[] args)        {            _Tasks = new Task[_TaskNum];            m_Semaphore = new SemaphoreSlim(MAX_RESOURCE);            int i = 0;            for (i = 0; i < _TaskNum; i++)            {                _Tasks[i] = Task.Factory.StartNew((num) =>                {                    var taskid = (int)num;                    Work1(taskid);                }, i);            }            var finalTask = Task.Factory.ContinueWhenAll(_Tasks, (tasks) =>            {                Task.WaitAll(_Tasks);                Console.WriteLine("==========================================================");                Console.WriteLine("All Phase is completed");                Console.WriteLine("==========================================================");            });            try            {                finalTask.Wait();            }            catch (AggregateException aex)            {                Console.WriteLine("Task failed And Canceled" + aex.ToString());            }            finally            {                m_Semaphore.Dispose();            }            Console.ReadLine();        }    }}

使用超时和取消

信号量当然不可能永久的阻塞在那里。信号量也提供了超时处理机制。方法是在Wait函数中传入一个超时等待时间 - Wait(int TIMEOUT)。当Wait返回值为false时表明它超时了。如果传入了 -1,则表示无限期的等待。

程序示例:注意其中的m_Semaphore.Release();已经被注释掉了,任务会等待1秒钟然后超时。

using System;using System.Text;using System.Threading;using System.Threading.Tasks;using System.Diagnostics;namespace Sample5_8_semaphoreslim{    class Program    {        private static int _TaskNum = 10;        private static Task[] _Tasks;        private const int MAX_RESOURCE = 3;        private const int RUN_LOOP = 10;        private static SemaphoreSlim m_Semaphore;        private static void Work1(int TaskID)        {            int i = 0;            var sw = Stopwatch.StartNew();            var rnd = new Random();            while (i < RUN_LOOP)            {                Thread.Sleep(rnd.Next(200, 500));                Console.WriteLine("TASK " + TaskID + " REQUESTing {");                if (!m_Semaphore.Wait(1000))                {                    Console.WriteLine("TASK " + TaskID + " TIMEOUT!!!");                    return;                }                try                {                    Console.WriteLine("TASK " + TaskID + " WOrking  ...  ..." + i);                    sw.Restart();                    Thread.Sleep(rnd.Next(2000, 5000));                }                finally                {                    Console.WriteLine("TASK " + TaskID + " REQUESTing }");                    //m_Semaphore.Release();                    i++;                }            }        }        static void Main(string[] args)        {            _Tasks = new Task[_TaskNum];            m_Semaphore = new SemaphoreSlim(MAX_RESOURCE);            int i = 0;            for (i = 0; i < _TaskNum; i++)            {                _Tasks[i] = Task.Factory.StartNew((num) =>                {                    var taskid = (int)num;                    Work1(taskid);                }, i);            }            var finalTask = Task.Factory.ContinueWhenAll(_Tasks, (tasks) =>            {                Task.WaitAll(_Tasks);                Console.WriteLine("==========================================================");                Console.WriteLine("All Phase is completed");                Console.WriteLine("==========================================================");            });            try            {                finalTask.Wait();            }            catch (AggregateException aex)            {                Console.WriteLine("Task failed And Canceled" + aex.ToString());            }            finally            {                m_Semaphore.Dispose();            }            Console.ReadLine();        }    }}

跨进程或AppDomain的同步

如果需要有跨进程或AppDomain的同步时,可以考虑使用Semaphore。Semaphore是取得的Windows 内核的信号量,所以在整个系统中是有效的。
它主要的接口时 Release和WaitOne,使用的方式和SemaphoreSlim是一致的。

0 0
原创粉丝点击