同时只允许Count个线程访问同一块区域的实现方式
好吧,后来才发现有Semaphore和SemaphoreSlim这两个类。
以前的答案:
最近.Net项目中用到了网页截图功能,这个截图功能是类似后台开了一个IE浏览器默默加载某个网页然后截取下来保存,因此并发截图量不能太大,但是又不能一个一个的截(因为截图函数里要设置等待网页加载时间,故一个一个截的话截完N个图要很长时间)。由此引出N个线程一次性只能让_concurrentSnapCount个线程进入截图区域。
一开始我是用一个计数器来计数截图区域进入了多少个线程,达到_concurrentSnapCount就不让进入,而阻塞部分用的是Monitor.Enter(_lkSnap);该部分代码:
/* 注:_lkSnap和_lkNum是object类型全局对象,_snapingCount是int类型初值为0的全局变量代表同时进入截图区域的线程数,_concurrentSnapCount是int类型常量且值大于1*/
// 一次性只有一个线程能获得Enter返回
Monitor.Enter(_lkSnap);
lock (_lkNum)
{
++_snapingCount; // 进入截图区域的线程数+1
if (_snapingCount < _concurrentSnapCount) // 判断进入截图区域的线程数是否达到设定最大值
{
Monitor.Exit(_lkSnap); // 没有达到最大值,Exit,让其它线程也能进入截图区域
}
}
// 一次性只能有_concurrentSnapCount个线程调用此函数
var img = WebPageSnapshot.WebSnapshot(postUrl, _snapshotWidth, delayTime); // 这块即为截图区域
lock(_lkNum)
{
// 判断此时在截图区域的线程数,如果等于最大值说明此时调用Enter会被阻塞,而次线程已经截图完毕可以是否截图区域的占用,故Exit。
if(_snapingCount == _concurrentSnapCount)
Monitor.Exit(_lkSnap); // 注意,Exit(obj)只能在Enter(obj)后调用一次,这也是为什么要判断_snapingCount==_concurrentSnapCount的原因
--_snapingCount; // 进入区域的线程数变量-1
}
DoOtherThing(...);
上面的代码是存在bug的,即下面的Monitor.Exit(_lkSnap)有可能产生异常信息:从不同步的代码块中调用了对象的同步方法。
比如说当_concurrentSnapCount值为2时,如果有三个线程要进入截图区域,第一个进入后由于_snapingCount < _concurrentSnapCount 为true故Monitor.Exit(...),
因此第二个线程Enter成功,但是_snapingCount < _concurrentSnapCount为false,故不执行Exit,因此第三个线程会被Enter阻塞。
我们假设第一个进入截图区域的线程也是第一个执行完WebSnapshot(...),该线程在判断_snapingCount == _concurrentSnapCount为true,故会执行Monitor.Exit(_lkSnap),由此
引发 从不同步的代码块中调用了对象的同步方法 的异常,因为最新的Enter(_lkSnap)是第二个线程执行的(或说_lkSnap的锁是由第二个线程加的),而下面的Exit(_lkSnap)却是由第一个线程执行,
故释放锁只能由加该锁的线程释放。如果只能由加锁的线程释放那么就变成了必须一次性进入截图区域的_concurrentSnapCount个线程全部执行完,然后由最后进入区域的线程释放锁,再进入下一批。
变成了分批进入而不是出一个进一个,这显然不和要求。
要做到符合要求的功能要将Monitor.Enter(_lkSnap)、Monitor.Exit(_lkSnap)改成由AutoResetEvent对象来实现,具体代码:
/*_autoRstEvt 也是全局AutoResetEvent对象,且initialState为true*/ _autoRstEvt.WaitOne(); // 首个线程进入将直接获得信号并自动执行Reset阻塞下一个线程
lock(_lkNum)
{
++_snapingCount;
if(_snapingCount < _concurrentSnapCount)
{
_autoRstEvt.Set(); // 未满,让下一个正在WaitOne的线程获得信号,或下一个将要WaitOne()的线程在WaitOne时直接获得信号。
}
}
// 一次性只能进入_concurrentSnapCount个
var img = WebPageSnapshot.WebSnapshot(postUrl, _snapshotWidth, delayTime); lock(_lkNum)
{
// 注意,_autoRstEvt可以重复Set,这点跟Monitor.Exit(obj)不一样,故此处判断其实没必要直接Set()就行。
if(_snapingCount == _concurrentSnapCount)
_autoRstEvt.Set();
--_snapingCount;
}
DoOtherThing(...);
至此,实现Count个线程并发进入某一区域且某个线程离开后进入新线程的功能完成。
同时只允许Count个线程访问同一块区域的实现方式的更多相关文章
- Winform之跨线程访问控件(在进度条上显示字体)
此文章对于遇到必须使用线程但是没有办法在线程内操作控件的问题的处理 有很好的解决方案(个人认为的.有更好的方案欢迎交流.) 在做跨线程访问之前我们先了解下我们所做的需要达到的效果: 这个是批量的将x ...
- [Winform]线程间操作无效,从不是创建控件的线程访问它的几个解决方案,async和await?
目录 概述 取消跨线程检查 使用委托异步调用 sync和await 总结 概述 最近在qq群里有一朋友,问起在winform中怎么通过开启线程的方式去处理耗时的操作,比如,查看某个目录下所有的文件,或 ...
- (委托事件处理)关于多线程执行显示进度条的实例(转)&&线程间操作无效: 从不是创建控件“rtxtEntryNO”的线程访问它。
关于多线程执行显示进度条的实例! 之前回答了一篇关于怎么在线程中操作进度条的帖子,估计有人看的不是很明白今天没事,写了一个小小的实例,很简单,就2个文件权当抛砖引玉,希望有更好解决方案的人发表一下意见 ...
- InvokeHelper,让跨线程访问/修改主界面控件不再麻烦(转)
http://bbs.csdn.net/topics/390162519 事实上,本文内容很简单且浅显,所以取消前戏,直接开始.. 源代码:在本文最后 这里是一张动画,演示在多线程(无限循环+Thre ...
- C#线程 访问资源同步简介
在多线程应用(一个或多个处理器)的计算中会使用到同步这个词.实际上,这些应用程序的特点就是它们拥有多个执行单元,而这些单元在访问资源的时候可能会发生冲突.线程间会共享同步对象,而同步对象的目的在于能够 ...
- Winform中子线程访问界面控件时被阻塞解决方案
public partial class WebData_Import : Form { //声明用于访问主界面的委托类型 public delegate void deleGetOrderdata( ...
- 线程间操作无效: 从不是创建控件“button1”的线程访问它。
.net2后是不能跨线程访问控件的.,窗体上的控件是当前线程创建的,当用户异步执行一个方法:在该方法中给窗体上的控件赋值,记住:当执行一个异步委托的时候,其实 就是开了一个线程去执行那个方法,这样就会 ...
- 【转】线程间操作无效: 从不是创建控件“textBox2” 的线程访问它。
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using Sy ...
- wpf(怎么跨线程访问wpf控件)
在编写代码时,我们经常会碰到一些子线程中处理完的信息,需要通知另一个线程(我这边处理完了,该你了). 但是当我们通知WPF的UI线程时需要用到Dispatcher. 首先我们需要想好在UI控件上需要显 ...
随机推荐
- webpack 3.X学习之Babel配置
Babel是什么 Babel是一个编译JavaScript的平台,它的强大之处表现在可以通过编译帮你达到: 使用下一代的javascript(ES6,ES7,--)代码,即使当前浏览器没有完成支持: ...
- Python测试开发之random模块
random模块是一个生成随机数.随机字符的模块,平时被使用的也非常多,下面是random模块的常用方法: random.random()生成一个0-1的随机小数,如果想要对随机小数保留两位小数,可以 ...
- NYOJ 417 死神来了 鸽巢原理
死神来了 时间限制:1000 ms | 内存限制:65535 KB 难度:3 描述 有一天,王小子在遨游世界时,遇到了一场自然灾害.一个人孤独的在一个岛上,没有吃的没有喝的.在他饥寒交迫将要死亡时 ...
- jQuery noConflict() 方法----与其他javaScript插件冲突时
1,全名代替----jQuery: $.noConflict(); jQuery(document).ready(function(){ jQuery("button").clic ...
- Sun 与 Oracle 合并的未来
引言 SUN 2008 初 10 亿美元收购 MySQL Oracle 2009 年 4 月 74 亿美元收购 SUN Sun 与 Oracle 合并的未来 1,如果云计算对企业来说变得越来越重要,那 ...
- AspNet Core Api Restful +Swagger 发布IIS 实现微服务之旅 (二)
上一步我们创建好CoreApi 接下来在框架中加入 Swagger 并发布 到 IIS (1)首先点击依赖项>管理Nuget包 (2)输入 Swashbuckle.aspnetCore 比 ...
- Linux定义变量的脚本
现有两段基本一样的代码,只是变量进行改变,其他都没有变化,但是执行过程中出现了不一样的结果 代码一: vi back.sh #backup import file,such as /etc/rc.lo ...
- java的热部署和热加载
ps:热部署和热加载其实是两个类似但不同的概念,之前理解不深,so,这篇文章重构了下. 一.热部署与热加载 在应用运行的时升级软件,无需重新启动的方式有两种,热部署和热加载. 对于Java应用程序来说 ...
- 如何在你的blog中添加炫酷的飘雪动画效果
将下面的代码复制到你的设置栏下页眉html代码框中即可 <script> (function($){$.fn.snow=function(options){,maxSize:,newOn: ...
- 中颖内带LED资源驱动代码
//上一篇写了LCD驱动,本篇写下LED驱动 //DISPCON 最高位为1时, 选择LED驱动,LCD驱动无效 最高位为0时, 选择LCD驱动.LED驱动无效 void Sh79fLed_Init( ...