同时只允许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控件上需要显 ...
随机推荐
- LSA和pLSA的比较
Comparison LSA pLSA 1. Theoretical background Linear Algebra Probabilities and Statistics 2. Objec ...
- ASP.NET Core教程【二】从保存数据看特有属性与服务端验证
前文索引: 在layout.cshtml文件中,我们可以看到如下代码: <a asp-page="/Index" class="navbar-brand" ...
- 基于FPGA的有限状态机浅析
前言:状态机大法好,状态机几乎可以实现一切时序逻辑电路. 有限状态机(Finite State Machine, FSM),根据状态机的输出是否与输入有关,可分为Moore型状态机和Mealy型状态机 ...
- Javascript-基础概念总结
[作用域]javascript中的作用域可以理解为一个语句执行的环境大小,有全局的作用域,函数作用域和eval作用域.在JS中没有块级作用域.讲到作用域,不得不讲执行环境,执行环境在JS中是最为重要的 ...
- 【面试问题】——秋招面试中遇到的一些问题&思维导图&反思
前言:秋招也跑了挺多的公司,虽然都是招Web前端,但是不同的公司,因为需求和目的不同,面试的考察点也是各不相同.我没有实习经验,只有自己学东学西比较杂也比较浅的一些知识积累可以用,这个过程我发现了自己 ...
- Mysql--触发器的操作
1.为什么使用触发器 2.创建触发器 2.1 创建有一条执行语句的触发器 例子:在数据库company中存在两个表对象:部门表(t_dept)和日记表(t_diary),创建触发器实现向部门表中插入记 ...
- 【C#入门教案-02】用记事本编写第一个C#程序-Hello World
02-用记事本编写第一个C#程序-Hello World 广东职业技术学院 欧浩源 [1]进行.NET程序开发的最基本环境配备 .NET Framework + 代码编辑工具(记事本或Noetpad ...
- npm install 时报错 Unexpected end of input at 1:15930
从github上clone代码后npm install,结果解决办法: npm config set registry https://registry.npm.taobao.org之后出现自动生成了 ...
- javascript设计模式——命令模式
前面的话 假设有一个快餐店,而我是该餐厅的点餐服务员,那么我一天的工作应该是这样的:当某位客人点餐或者打来订餐电话后,我会把他的需求都写在清单上,然后交给厨房,客人不用关心是哪些厨师帮他炒菜.餐厅还可 ...
- 将PDF文件中按页截取,并以流的形势返回给客户端
#需要用到pyPdf库 from pyPdf import PdfFileWriter,PdfFileReader from django.http import HttpResponse try: ...