ManualResetEvent实现线程的暂停与恢复
背景
前些天遇到一个需求,在没有第三方源码的情况下,刷新一个第三方UI,并且拦截到其ajax请求的返回结果。当结果为AVALIABLE的时候,停止刷新并语音提示,否则继续刷新。
分析这个需求,发现需要控制一个刷新循环的暂停与开始,因此网上搜到了通过ManualResetEvent实现线程的暂停与恢复。
ManualResetEvent介绍
ManualResetEvent是一个通过信号机制,实现线程间状态同步的类。常用的方法有以下三个:
WaitOne:阻止当前线程,直到收到信号
Reset:将事件状态设置为非终止状态,导致线程阻止
Set:将事件状态设置为终止状态,从而允许继续执行一个或多个等待线程
实现设计
1.通过CefSharp的谷歌浏览器插件请求第三方网站

2.具体交互逻辑如下
- 默认开启一个线程,并通过WaitOne挂起,等待手动开始自动刷新的指令
- 手动通过Set方法发送开始工作信号
- 执行完面操作后,通过Reset将线程再次挂起,并等待Ajax结果,判断Ajax结果,如果不等于AVALIABLE则再次恢复线程
public Form1()
{
InitializeComponent();
var setting = new CefSettings();
setting.Locale = "zh-CN";
setting.UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36";
// 避免页面加载不出来, 建议加上这句
Cef.Initialize(setting); workerThread = new Thread(new ThreadStart(BeginListenWorkerAsync));
workerThread.IsBackground = true;
workerThread.Start(); this.停止自动监听ToolStripMenuItem.Enabled = false;
} private async void BeginListenWorkerAsync()
{
while (true)
{
_eventBeginListenWorkList.WaitOne(); //1.默认挂起线程,等待信号
chromiumWebBrowser1.GetBrowser().MainFrame.ExecuteJavaScriptAsync(@"document.evaluate(""//*[@id='root']/div[1]/div[2]/div/div[2]/div[1]/div[1]/div/div[1]/button"", document).iterateNext().click()"); Thread.Sleep(1000);
chromiumWebBrowser1.GetBrowser().MainFrame.ExecuteJavaScriptAsync(@"document.evaluate(""//*[@id='root']/div[1]/div[2]/div/div[1]/div/div/div/div[2]/div[6]/div/button"", document).iterateNext().click()");
_eventBeginListenWorkList.Reset(); //3.页面执行完相应操作则再次挂起线程,等待ajax请求结果再判断是否恢复线程
}
} private void 停止自动监听ToolStripMenuItem_Click(object sender, EventArgs e)
{
this.开始自动监听ToolStripMenuItem.Enabled = true;
this.停止自动监听ToolStripMenuItem.Enabled = false;
_eventBeginListenWorkList.Reset();
StartListen = false;
} private void 开始自动监听ToolStripMenuItem_Click(object sender, EventArgs e)
{
this.开始自动监听ToolStripMenuItem.Enabled = false;
this.停止自动监听ToolStripMenuItem.Enabled = true;
_eventBeginListenWorkList.Set(); // 2.手动通过Set发送信号恢复线程,让其工作
StartListen = true;
}
3.现在我们需要定义RequestHandler来指定ResourceHandler拦截ajax请求
private void Form1_Load(object sender, EventArgs e)
{
chromiumWebBrowser1.RequestHandler = new MyRequestHandler(this);
chromiumWebBrowser1.Load("https://www.xxxxxxxx.com/");
}
MyRequestHandler中需要指定自定义ResourceHandler
public class MyRequestHandler : RequestHandler
{
Form1 _form;
public MyRequestHandler(Form1 form)
{
_form = form;
}
public static string AuthorizationValue;
protected override bool OnBeforeBrowse(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, bool userGesture,
bool isRedirect)
{
// 先调用基类的实现,断点调试
return base.OnBeforeBrowse(chromiumWebBrowser, browser, frame, request, userGesture, isRedirect);
} protected override IResourceRequestHandler GetResourceRequestHandler(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame,
IRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling)
{
Console.WriteLine(request.Url);
if (request.Url.StartsWith("https://www.xxxxxxxxxxxxxxxx/api") && request.Headers.AllKeys.Contains("Authorization"))
{
AuthorizationValue = request.Headers["Authorization"];
} return new MyResourceRequestHandler(_form);
}
}
4.最后ResourceRequestHandler拦截到ajax请求之后,如果结果不是AVALIABLE则通过StartListenFunc再次恢复线程
public class MyResourceRequestHandler : ResourceRequestHandler
{
Form1 _form;
public MyResourceRequestHandler(Form1 form)
{
_form = form;
}
private Dictionary<ulong, MemoryStreamResponseFilter> responseDictionary = new Dictionary<ulong, MemoryStreamResponseFilter>();
protected override IResponseFilter GetResourceResponseFilter(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response)
{
var dataFilter = new MemoryStreamResponseFilter();
responseDictionary.Add(request.Identifier, dataFilter);
return dataFilter;
} protected override void OnResourceLoadComplete(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response, UrlRequestStatus status, long receivedContentLength)
{
MemoryStreamResponseFilter filter;
if (responseDictionary.TryGetValue(request.Identifier, out filter))
{
var data = filter.Data; if (request.Url.StartsWith("https://xxxxxxxxxxxxxxxxxxxx/oneapi"))
{
var available = false;
string s = System.Text.Encoding.UTF8.GetString(data, 0, data.Length);
Console.WriteLine(s);
if (!string.IsNullOrEmpty(s))
{
var responseData = JsonConvert.DeserializeObject<ScheduleResponse>(s);
if (responseData != null && responseData.productPreviews != null)
{
foreach (var item in responseData.productPreviews)
{
if (item.schedule != null && item.schedule.status != null && item.schedule.status.name == "AVAILABLE")
{
available = true;
Mp3Player.Play();
Action act = delegate () { _form.StopListen(); };
_form.Invoke(act);
break;
}
}
}
} if (!available && Form1.StartListen)
{
Form1.StartListenFunc();
}
} filter.Dispose();
}
}
}
而StartListenFunc则是通过Set方法再次恢复了线程对ui的重复查询
public static void StartListenFunc()
{
_eventBeginListenWorkList.Set();
StartListen = true;
}
ManualResetEvent官方介绍如下
https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.manualresetevent?view=netframework-1.1
ManualResetEvent实现线程的暂停与恢复的更多相关文章
- 利用ManualResetEvent来来控制异步调用的打印的线程的暂停和恢复(转)
利用ManualResetEvent来来控制异步调用的打印的线程的暂停和恢复 打印过程可能很长,这时候有可能需要暂停下来做一些事情,然后回来继续接着打印 打印过程中有2个线程:一个是程序运行的主线程, ...
- C# 线程的暂停和恢复的 实现
我们可以通过ManualResetEvent类来实现. 声明, 初始化时不执行 private static ManualResetEvent _eventWorkList = new ManualR ...
- C# winform 多线程异步操作线程启动暂停与恢复
/// <summary> /// 线程控制模块 /// </summary> private ManualResetEvent manualResetEvent = new ...
- 使用 suspend 和 resume 暂停和恢复线程
suspend 和 resume 的使用 在 Thread 类中有这样两个方法:suspend 和 resume,这两个方法是成对出现的. suspend() 方法的作用是将一个线程挂起(暂停), r ...
- gdb暂停或恢复程序的运行
ref : https://blog.csdn.net/seu_lyr/article/details/9050657 一 暂停程序的运行: (一)GDB的暂停方式:断点(BreakPoint). ...
- java并发编程(三)线程挂起,恢复和终止的正确方法
转载请注明出处:http://blog.csdn.net/ns_code/article/details/17095733 下面我们给出不用上述两个方法来实现线程挂起和恢复的策略--设置标志位. ...
- Quartz的任务的临时启动和暂停和恢复
Quartz的任务的临时启动和暂停和恢复 在项目中需要手动启停某些服务,那么需要有一个控制这些任务的类.由于任务是有Quartz控制的,我们只需要通过Quartz的相关的API实现相关的功能即可. p ...
- 暂停和恢复Activity Android
暂停和恢复Activity(Pausing and Resuming an Activity) 在正常的应用程序使用,前台activity有时会被其他可视化组件遮挡,从而 造成activity的暂停. ...
- 【Android Developers Training】 16. 暂停和恢复一个Activity
注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...
随机推荐
- jsp部分乱码(并不是没有设置编码的问题)
当页面跳转的时候,还是有中文的乱码存在,最后发现我没有写页面的提交方式, <form action = "show.jsp"> 用户名:<input type=& ...
- ELK 使用filebeat替代Logstash收集日志
使用beats采集日志 之前也介绍过beats是ELK体系中新增的一个工具,它属于一个轻量的日志采集器,以上我们使用的日志采集工具是logstash,但是logstash占用的资源比较大,没有beat ...
- 【LeetCode】1133. Largest Unique Number 解题报告(C++)
作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 桶排序 日期 题目地址:https://leetcod ...
- 【LeetCode】1037. Valid Boomerang 解题报告(C++)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 中学数学题 日期 题目地址:https://leet ...
- B. Recover the String
B. Recover the String time limit per test 1 second memory limit per test 256 megabytes input standar ...
- 关于wlw连接wordpress的问题
前几天搭建好wordpress博客网站后,一直想和博客园一样,使用wlw发布文章.无奈遇到了难题,一直没有办法解决. 今天我看到一篇博客,遇到问题和我类似:尝试连接到您的日志时出错:基础连接已经关闭: ...
- Auto-Encoding Variational Bayes
目录 主要内容 Encoder (损失part1) Decoder (损失part2) 伯努利分布 高斯分布 代码 Kingma D P, Welling M. Auto-Encoding Varia ...
- 5分钟搭建wordpress个人博客网站——宝塔傻瓜式部署,无坑系列,附赠主题和md插件[2021-12-31]
一.前言 自从买了服务器,小编已经马不停蹄的学了两天服务搭建的知识,问了很多大佬,快速搭建自己的博客网站.有四种方式,我在这里全部分享给大家.自己已经搭建好,欢迎大家过来看一下,给你提供个思路哈! 小 ...
- JZOJ5409. Fantasy && Luogu2048 [NOI2010]超级钢琴
题目大意 给出一个序列和\(L, R\), 求前k大长度在\([L,R]\)之间的连续子序列的和的和. 解题思路 朴素的想法是对于一个左端点\(p\), 它的右区间取值范围是一个连续的区间即\([p+ ...
- minio文件上传与下载
目录 一.minio简介 二.minio安装 一.java中使用 一.minio简介 MinIO 是在 GNU Affero 通用公共许可证 v3.0 下发布的高性能对象存储. 它是与 Amazon ...