C#多线程系列之资源池限制_第1页
C#多线程系列之资源池限制_第2页
C#多线程系列之资源池限制_第3页
C#多线程系列之资源池限制_第4页
C#多线程系列之资源池限制_第5页
已阅读5页,还剩4页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第C#多线程系列之资源池限制两者都可以限制同时访问某一资源或资源池的线程数。

这里先不扯理论,我们从案例入手,通过示例代码,慢慢深入了解。

Semaphore类

这里,先列出Semaphore类常用的API。

其构造函数如下:

构造函数说明Semaphore(Int32,Int32)初始化Semaphore类的新实例,并指定初始入口数和最大并发入口数。Semaphore(Int32,Int32,String)初始化Semaphore类的新实例,并指定初始入口数和最大并发入口数,根据需要指定系统信号灯对象的名称。Semaphore(Int32,Int32,String,Boolean)初始化Semaphore类的新实例,并指定初始入口数和最大并发入口数,还可以选择指定系统信号量对象的名称,以及指定一个变量来接收指示是否创建了新系统信号量的值。

Semaphore使用纯粹的内核时间(kernel-time)方式(等待时间很短),并且支持在不同的进程间同步线程(像Mutex)。

Semaphore常用方法如下:

方法说明Close()释放由当前WaitHandle占用的所有资源。OpenExisting(String)打开指定名称为信号量(如果已经存在)。Release()退出信号量并返回前一个计数。Release(Int32)以指定的次数退出信号量并返回前一个计数。TryOpenExisting(String,Semaphore)打开指定名称为信号量(如果已经存在),并返回指示操作是否成功的值。WaitOne()阻止当前线程,直到当前WaitHandle收到信号。WaitOne(Int32)阻止当前线程,直到当前WaitHandle收到信号,同时使用32位带符号整数指定时间间隔(以毫秒为单位)。WaitOne(Int32,Boolean)阻止当前线程,直到当前的WaitHandle收到信号为止,同时使用32位带符号整数指定时间间隔,并指定是否在等待之前退出同步域。WaitOne(TimeSpan)阻止当前线程,直到当前实例收到信号,同时使用TimeSpan指定时间间隔。WaitOne(TimeSpan,Boolean)阻止当前线程,直到当前实例收到信号为止,同时使用TimeSpan指定时间间隔,并指定是否在等待之前退出同步域。

我们来直接写代码,这里使用《原子操作Interlocked》中的示例,现在我们要求,采用多个线程执行计算,但是只允许最多三个线程同时执行运行。

使用Semaphore,有四个个步骤:

new实例化Semaphore,并设置最大线程数、初始化时可进入线程数;

使用.WaitOne();获取进入权限(在获得进入权限前,线程处于阻塞状态)。

离开时使用Release()释放占用。

Close()释放Semaphore对象。

《原子操作Interlocked》中的示例改进如下:

classProgram

//求和

privatestaticintsum=0;

privatestaticSemaphore_pool;

//判断十个线程是否结束了。

privatestaticintisComplete=0;

//第一个程序

staticvoidMain(string[]args)

Console.WriteLine("执行程序");

//设置允许最大三个线程进入资源池

//一开始设置为0,就是初始化时允许几个线程进入

//这里设置为0,后面按下按键时,可以放通三个线程

_pool=newSemaphore(0,3);

for(inti=0;ii++)

Threadthread=newThread(newParameterizedThreadStart(AddOne));

thread.Start(i+1);

Console.ForegroundColor=ConsoleColor.Red;

Console.WriteLine("任意按下键(不要按关机键),可以打开资源池");

Console.ForegroundColor=ConsoleColor.White;

Console.ReadKey();

//准许三个线程进入

_pool.Release(3);

//这里没有任何意义,就单纯为了演示查看结果。

//等待所有线程完成任务

while(true)

if(isComplete=10)

break;

Thread.Sleep(TimeSpan.FromSeconds(1));

Console.WriteLine("sum="+sum);

//释放池

_pool.Close();

publicstaticvoidAddOne(objectn)

Console.WriteLine($"线程{(int)n}启动,进入队列");

//进入队列等待

_pool.WaitOne();

Console.WriteLine($"第{(int)n}个线程进入资源池");

//进入资源池

for(inti=0;ii++)

Interlocked.Add(refsum,1);

Thread.Sleep(TimeSpan.FromMilliseconds(500));

//解除占用的资源池

_pool.Release();

isComplete+=1;

Console.WriteLine($"第{(int)n}个线程退出资源池");

}

看着代码有点多,快去运行一下,看看结果。

实例化Semaphore使用了newSemaphore(0,3);,其构造函数原型为

publicSemaphore(intinitialCount,intmaximumCount);

initialCount表示一开始允许几个进程进入资源池,如果设置为0,所有线程都不能进入,要一直等资源池放通。

maximumCount表示最大允许几个线程进入资源池。

Release()表示退出信号量并返回前一个计数。这个计数指的是资源池还可以进入多少个线程。

可以看一下下面的示例:

privatestaticSemaphore_pool;

staticvoidMain(string[]args)

_pool=newSemaphore(0,5);

_pool.Release(5);

newThread(AddOne).Start();

Thread.Sleep(TimeSpan.FromSeconds(10));

_pool.Close();

publicstaticvoidAddOne()

_pool.WaitOne();

Thread.Sleep(1000);

intcount=_pool.Release();

Console.WriteLine("在此线程退出资源池前,资源池还有多少线程可以进入?"+count);

}

前面我们学习到Mutex,这个类是全局操作系统起作用的。我们从Mutex和Semphore中,也看到了信号量这个东西。

信号量分为两种类型:本地信号量和命名系统信号量。

命名系统信号量在整个操作系统中均可见,可用于同步进程的活动。局部信号量仅存在于进程内。

当name为null或者为空时,Mutex的信号量时局部信号量,否则Mutex的信号量是命名系统信号量。

Semaphore的话,也是两种情况都有。

如果使用接受名称的构造函数创建Semaphor对象,则该对象将与该名称的操作系统信号量关联。

两个构造函数:

Semaphore(Int32,Int32,String)

Semaphore(Int32,Int32,String,Boolean)

上面的构造函数可以创建多个表示同一命名系统信号量的Semaphore对象,并可以使用OpenExisting方法打开现有的已命名系统信号量。

我们上面使用的示例就是局部信号量,进程中引用本地Semaphore对象的所有线程都可以使用。每个Semaphore对象都是单独的本地信号量。

SemaphoreSlim类

SemaphoreSlim跟Semaphore有啥关系?

微软文档:

SemaphoreSlim表示对可同时访问资源或资源池的线程数加以限制的Semaphore的轻量替代。

SemaphoreSlim不使用信号量,不支持进程间同步,只能在进程内使用。

它有两个构造函数:

构造函数说明SemaphoreSlim(Int32)初始化SemaphoreSlim类的新实例,以指定可同时授予的请求的初始数量。SemaphoreSlim(Int32,Int32)初始化SemaphoreSlim类的新实例,同时指定可同时授予的请求的初始数量和最大数量。

我们改造一下前面Semaphore中的示例:

classProgram

//求和

privatestaticintsum=0;

privatestaticSemaphoreSlim_pool;

//判断十个线程是否结束了。

privatestaticintisComplete=0;

staticvoidMain(string[]args)

Console.WriteLine("执行程序");

//设置允许最大三个线程进入资源池

//一开始设置为0,就是初始化时允许几个线程进入

//这里设置为0,后面按下按键时,可以放通三个线程

_pool=newSemaphoreSlim(0,3);

for(inti=0;ii++)

Threadthread=newThread(newParameterizedThreadStart(AddOne));

thread.Start(i+1);

Console.WriteLine("任意按下键(不要按关机键),可以打开资源池");

Console.ReadKey();

_pool.Release(3);

//这里没有任何意义,就单纯为了演示查看结果。

//等待所有线程完成任务

while(true)

if(isComplete=10)

break;

Thread.Sleep(TimeSpan.FromSeconds(1));

Console.WriteLine("sum="+sum);

//释放池

publicstaticvoidAddOne(objectn)

Console.WriteLine($"线程{(int)n}启动,进入队列");

//进入队列等待

_pool.Wait();

Console.WriteLine($"第{(int)n}个线程进入资源池");

//进入资源池

for(inti=0;ii++)

Interlocked.Add(refsum,1);

Thread.Sleep(TimeSpan.FromMilliseconds(200));

//解除占用的资源池

_pool.Release();

isComplete+=1;

Console.WriteLine($"第{(int)n}个线程退出资源池");

}

SemaphoreSlim不需要Close()。

两者在代码上的区别是就这么简单。

如果使用下面的构造函数实例化Semaphor(参数name不能为空),那么创建的对象在整个操作系统内都有效。

publicSemaphore(intinitialCount,intmaximumCount,stringname);

Semaphorslim则只在进程内内有效。

SemaphoreSlim类不会对Wait、WaitAsync和Release方法的调用强制执行线程或任务标识。

而Semaphor类,会对此进行严格监控,如果对应调用数量不一致,会出现异常。

此外,如果使用Sema

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论