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

概要

当多个任务或线程并行运行时,难以避免的对某些有限的资源进行并发的访问。可以考虑使用信号量来进行这方面的控制(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是一致的。

时间: 2024-08-13 08:22:33

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

C#并行编程-并发集合

原文:C#并行编程-并发集合 菜鸟学习并行编程,参考<C#并行编程高级教程.PDF>,如有错误,欢迎指正. 背景 基于任务的程序设计.命令式数据并行和任务并行都要求能够支持并发更新的数组.列表和集合. 在.NET Framework 4 以前,为了让共享的数组.列表和集合能够被多个线程更新,需要添加复杂的代码来同步这些更新操作. 如您需要编写一个并行循环,这个循环以无序的方式向一个共享集合中添加元素,那么必须加入一个同步机制来保证这是一个线程安全的集合. System.Collenctions

C# 并行编程 之 并发集合 (.Net Framework 4.0)(转)

转载地址:http://blog.csdn.net/wangzhiyu1980/article/details/45497907 此文为个人学习<C#并行编程高级教程>的笔记,总结并调试了一些文章中的代码示例. 在以后开发过程中可以加以运用. 对于并行任务,与其相关紧密的就是对一些共享资源,数据结构的并行访问.经常要做的就是对一些队列进行加锁-解锁,然后执行类似插入,删除等等互斥操作. .NetFramework 4.0 中提供了一些封装好的支持并行操作数据容器,可以减少并行编程的复杂程度.

C# 并行编程 之 并发集合 (.Net Framework 4.0)

此文为个人学习<C#并行编程高级教程>的笔记,总结并调试了一些文章中的代码示例. 在以后开发过程中可以加以运用. 对于并行任务,与其相关紧密的就是对一些共享资源,数据结构的并行访问.经常要做的就是对一些队列进行加锁-解锁,然后执行类似插入,删除等等互斥操作. .NetFramework 4.0 中提供了一些封装好的支持并行操作数据容器,可以减少并行编程的复杂程度. 基本信息 .NetFramework中并行集合的名字空间: System.Collections.Concurrent 并行容器:

[笔记][Java7并发编程实战手册]3.2 资源的并发访问控制Semaphore信号量

[笔记][Java7并发编程实战手册]系列目录 简介 本文学习信号量Semaphore机制. Semaphore 本质是一个共享锁 内部维护一个可用的信号集,获取信号量之前需要先申请获取信号数量:用完之后,则需要释放信号量:如果不释放,那么其他等待线程则一直阻塞直到获取信号量或则被中断为止 本人的理解是:互斥锁是同一时间只能一个线程访问,而在这里,是同一时间允许获取到了信号量的线程并发访问,而没有获取到信号量的则必须等待信号量的释放: 将信号量初始化为 1,使得它在使用时最多只有一个可用的许可,

iOS 并行编程:Operation Queues

1 简介 1.1 功能        Operation Queue也是IOS的一种并行编程技术,类似Dispatch Queue可以帮助用户管理多线程.但是Operation Queue将任务封装在NSOperation对象中,从而可以更好的控制任务的执行.并且Dispatch Queue的先入先出的执行方式不同,Operation Queue任务的执行顺序可以控制.其中IOS是将任务交给NSOperation对象进行管理,其中NSOperation是个抽象类,必须被继承,目前系统预定义了两个

OpenCL学习笔记(二):并行编程概念理解

欢迎转载,转载请注明:本文出自Bin的专栏blog.csdn.net/xbinworld. 技术交流QQ群:433250724,欢迎对算法.技术.应用感兴趣的同学加入. 并行编程的需求是显而易见的,其最大的难题是找到算法的并行功能,同时必须处理数据的共享和同步.但是,因为每一个算法都是不一样的,很难有通用的并行功能--粒度都有可能是不一样的.OpenCL提供了很多并行的抽象模型,因此算法开发人员可以在不同粒度上开发并行的算法,以及数据的共享和同步. 一般来说,并行编程有两种大类型--分散收集(s

python并行编程

并行编程的思想:分而治之,有两种模型 1.MapReduce:将任务划分为可并行的多个子任务,每个子任务完成后合并得到结果 例子:统计不同形状的个数. 先通过map进行映射到多个子任务,分别统计个数,然后在用reduce进行归纳一下. 2.流水:将任务分为串行的多个子任务,每个子任务并行.ProductConsume 例子: 多个生产者进行并行,多个消费者进行并行.生产者生产出来东西放到队列里:队列里有东西时,消费者就可以进行消费,这样双方没有太大的依赖关系. 为什么要并行编程呢? 多核,云计算

理解并行编程

并行编程从业务实现的角度可分为数据并行与任务并行,也就是要解决的问题是以数据为核心还是以要处理的事情为核心.基于任务的并行编程模型TPL(任务并行库)是从业务角度实现的并行模型,它以System.Threading.Tasks命名空间下的Parallel类为实现核心类,优点是不需要我们考虑不同的硬件差异,只需要重点关注所实现的任务. 1.任务并行库TPL TPL主要包括数据并行和任务并行,无论是数据并行还是任务并行,都可以使用并行查询PLINQ提高数据查询的效率.数据并行是对数据集合中的元素同时

.NET并行编程1 -

设计模式--.net并行编程,清华大学出版的中译本. 相关资源地址主页面: http://parallelpatterns.codeplex.com/ 代码下载: http://parallelpatterns.codeplex.com/releases/view/50473 书籍在线地址: https://msdn.microsoft.com/en-us/library/ff963553.aspx 使用并行编程的一些示例: https://code.msdn.microsoft.com/Par