同时只允许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控件上需要显 ...
随机推荐
- Liunx文件解压与压缩
文件压缩和解压缩 常见压缩格式如下 .zip .gz .bz2 .tar.gz .tar.gz2 .zip压缩 zip 压缩文件名 源文件 压缩文件 zip -r(递归) 压缩文件名 源目录 压缩目录 ...
- 4)C语言指针(C自考学习)
指针和指针变量 指针就是地址,地址是一种数据类型.指针变量也是变量,但只能存放地址类型的数据,可以称为"地址型"变量. 1)内存单元和地址 一个程序运行时,程序本身和程序中用到的数 ...
- 从今天起开始记录下在freecodecamp学习的一些tip吧(所有内容都在这个随笔的评论里面记录)
因为可能东西会很零碎 所以就放在随笔里吧 当需要在字符串中使用一个: " 或者 ' 时 可以通过在引号前面使用 反斜杠 (\) 来转义引号. var sampleStr = "Al ...
- 九、Hadoop学习笔记————Hive简介
G级别或者T级别都只能用hadoop
- 在本地搭建play-with-docker
安装play-with-docker 官方Github地址 https://github.com/play-with-docker/play-with-docker 本人Github地址 https: ...
- jQuery中使用$.each()遍历后台响应的json字符串问题
今天在做练习项目的时候,使用$.each()方法遍历后台传过来的json字符串时,chrome浏览器中发现如下问题 Cannot use 'in' operator to search for 'l ...
- js的学习(window对象的使用)
open方法: //语法:var winObj = window.open([url][,name][,options]); //参数:url:准备在新窗口中显示那个文件.url可以为空字符串,表示 ...
- js实现谷歌网站统计
基本方法 function ga() { if (window.ga) { window.ga.apply(null, arguments); } else { stack.push(argument ...
- php综合运用技术
五.PHP综合应用 1.写出下列服务的用途和默认端口(新浪网技术部) ftp.ssh.http.telnet.https ftp:File Transfer Protocol,文件传输协议,是应用层的 ...
- 制作多级菜单hide()与show() toggle()
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...