今天,在群里向大家请教了这样一个问题:“两个对象(类、窗体或什么)之间,要完成比较频繁的报告进度更新都有哪些好的方式”,Somebody 跳出来给出了个“IProgress”,没了解过,后面围绕着它讨论学习了下。

简单来说,IProgress 是类库给出的一种解决问题的方式而非具体实现,IProgress 包括一个需要实现的 Report 方法。使用时由调用“任务”的“创建者”创建对接口的实现,也就是具体实现 Report 的方法,此时创建者便可以具体控制要如何报告、报告什么,例如在 Report 中写上这么一句简单粗暴欠揍的“ textBox1.Text = currentProgress.ToString()”,但创建者控制不了的是“什么时间报告”,因为这是由“任务”本身设定的。创建者将这个具体实现传递给“任务”,任务在其逻辑内(某个属性内或某个方法内)加上一句 IProgress.Report(xxx),实现了“什么时间报告”。

在这个逻辑中,创建者可以自由控制并专注实现如何报告,而不用关心何时接到报告(事实上,创建者也没法关心,因为何时调用 Report 方法是任务决定的,并且创建者也不必亲自“接收”报告,因为报告的方式创建者已经实现了:textBox1...);而任务专注于何时报告进度更为合理,而不用关心怎么样报告,只要在需要报告的时候调用 Report 方法就好。好处就是各管各的,看似比较合理。

Somebody Ivony 还是提醒了这种方式类似于事件的隐患,即“调用了IProgress接口后,任务抓住了IProgress对象的引用,这一点和事件是一样的,所以应当设计轻量级的IProgress对象,并且与Worker的调用方解耦”。

感谢大家和 Ivony 的解答!

试试手气  9:45:05
请教大家个事儿,如果有个类暂且叫做 worker,这个 worker 要完成一些工作(不用管具体干什么),重要的是他要对工作的进度做频繁的更新和报告(没有频繁到高大上的地步),要使其它类或对象了解 worker 的进度都有哪些比较好的机制,worker自身事件?让其它类访问 worker 的公开只读属性? 上次Ivony说到事件的坑,就想到了这个事儿 ♪慕斯⌒。²º¹⁴ 9:48:45 Ivony 说的是.NET内部控件的事件是个坑吧。 试试手气 9:50:07
控件的事件和自定义事件有区别吗? X 9:52:32
有些细节别太在意 试试手气 9:53:35
那好,那我就向大家请教一下都有哪些机制可以完成任务吧 ^^ Ivony... 9:54:24
IProgress 试试手气 9:58:25
让 worker继承 IProgress 吗? Ivony... 10:01:49
不是,你可以在MSDN上搜索相关示例。。。。 试试手气 10:01:59
我正在查看 IProgress,Ivony Ivony... 10:02:39
IProgress接口的用法是由调用任务的程序创建,任务会调用这个接口的方法来反向通知进度。 试试手气 10:38:36
void Factory()
{
var mem = new Worker();
mem.Work(new IProgress<int>());
} pubulic class Worker{
void Work(IProgress<int> progress)
{
工作逻辑();
progress.Report(i++); // 报告工作
}
} 试试手气 10:45:51
我刚才写的那个伪代码的逻辑对么,是你说的意思吗? Ivony... 10:46:09
是的,微软推荐的方案就是这样。。。。
不过这个你也可以仅做参考,这样反转通知是避免事件陷阱的一种方式。 Ivony... 10:47:20
因为反转之后,变成了Worker依赖于调用方,而调用方肯定比Worker活得久,所以自然没有属性的问题。
因为反转之后,变成了Worker依赖于调用方,而调用方肯定比Worker活得久,所以自然没有事件的问题。
而且IProgress<T>是可以与调用方解耦的,互相没有牵连。 倉ル 10:48:41 Ivony... 10:49:49
我好像想错了,刚才的请忽略,我要开会去了。。。。 试试手气 10:50:26
但我感觉里边好像缺少了一个我还没理解的,非常关键的 key 的东西,就是
怎么就“报告进度”了。请忙 倉ル 10:51:30
好像是在你 work 里工作的每个阶段 调用report 吧 试试手气 11:01:38
Report 是 IProgress 接口的唯一一个方法,就是用来报告进度的。什么时候调用 Report 不是问题,我还没理解 Report 方法怎么就向其父级 Factory 报告进度了?还是……难道……莫非……Report 并不是向 Factory 作报告的 倉ル 11:03:49
像谁报告 你是说的算的吧
你在调用work 的时候指定 报告接收者的吧 X 11:04:16
应该是取决于你怎样实现IProgress这个接口的 Ivony... 11:09:25
是的,,,,,
譬如说你可以把IProgress绑定到一个进度条上。 试试手气 11:17:57
恩,X 说的是我还没融会贯通的节点……let me see see
1 -> IProgress 既然是个接口,那使用之前应该先去实现它
2 -> 实现IProgress的对象,里边需要实现 Report 方法,所以,方法里边会明确写明“如何更新”,比如Report{ textBox1.Text = i.ToString();}
3 -> Factory 把自己创建的实现接口的这个对象给到 Worker,让 Worker 来报告进度 所以,Factory 不用管什么时候报告,是由 Worker 决定的;而 Worker 不用管具体 Factory 需要如何报告,只知道自己调用 Report 方法,Factory就能收到了 Right? Ivony... 11:19:02
Right。 你想一下,其实这个和事件是一样的。 试试手气 11:19:16
对,很类似 Ivony... 11:19:21
也就是说事件和这种类型的接口是等价的。
我之前说错了是这种结构其实没有从根本上解决事件的内存泄露问题。 Ivony... 11:21:03
回想一下事件的内存泄露怎么造成的,事件宿主会抓住所有挂接的委托的引用,委托抓住方法所属实例的引用。而事件宿主不能尽快释放就会挂掉。
用IProgress接口后,Worker抓住了IProgress对象的引用,这一点和事件是一样的,所以应当设计轻量级的IProgress对象,并且与Worker的调用方解耦。 试试手气 11:22:55
但有个问题,假如 Factory 是个窗体,需要更新进度显示在 textBox1 就像我刚才举的例子。而 Worker 是个类,假设 Factory 定义的 Report 方法里写上 textBox1.Text = xxx ,这不就是个很大的问题吗? 你的意思是,关键在于如何实现一个轻量级、解耦的 IProgress 实现。 Ivony... 11:25:17
Report方法里面写什么无关紧要,关键是你的IProgress实现类的字段。
这个类型的实例会被Worker抓住,所以其字段在Worker被释放前都不能释放。
其实呢你的Worker如果生命周期很短的话是不用考虑这个问题的。 Ivony... 11:26:20
事件的陷阱主要出现在那个Timer上,这货是个无限生命周期的东西。
属于设计失误。 试试手气 11:28:10
但假如 worker 是个周期长,长时间后台运行的东东就会增加风险 Ivony... 11:28:37
是的,,,, 试试手气 11:30:16
但很多情形,worker 都很可能是个周期长、长时间后台运行的东东。比如后台持续下载队列中的文件、持续读取或写入磁盘等 这时候,是不是就需要对 worker 自身重新审视和设计了 Ivony... 11:30:55
嗯,,,,其实有这个意识就好了,过分的纠结于这一点反倒无益。
长和短都是相对而言的。
即使生命周期很长,如果只挂一个Progress上去也没事,你不停的挂才会有事。 试试手气 11:31:51
嗯 Ivony... 11:31:57
事件的问题就在于大家经常无止尽的挂事件上去,从来不解挂,,,,
举另一个例子吧,,,,
创建一个WebClient,使用基于事件的异步。。。。 Ivony... 11:33:04
你先挂一个方法在Completed,然后导航到某个地址,加载完后会引发Completed事件。
然后你又挂一个方法到Completed,再导航,,,,
如此反复,挂了,,,,, X 11:35:37
事实上应该很少有人会对同一个对象重复注册多个相同的事件吧 Ivony... 11:36:52
Timer就会啊,,,, X 11:37:50
Timer也是只注册了一次吧 试试手气 11:45:18
去记录下这次的对话

View Dialog

哦对了,在博客园找到一篇比较直观的帖子作为推荐,作者 Saar,点击查看 => 帖子原文

点击查看 => 来自 Microsoft Blog 的帖子

从异步更新进度想起的事儿——IProgress的更多相关文章

  1. android AsyncTask异步下载并更新进度条

    AsyncTask异步下载并更新进度条    //如果不是很明白请看上篇文章的异步下载 AsyncTask<String, Integer, String> 第一个参数:String 传入 ...

  2. WPF BackGroundWord 异步加载更新进度条示例

    <Window x:Class="AsynchronousLoading.MainWindow" xmlns="http://schemas.microsoft.c ...

  3. Winform实现多线程异步更新UI(进度及状态信息)

    引言 在进行Winform程序开发需要进行大量的数据的读写操作的时候,往往会需要一定的时间,然在这个时间段里面,界面ui得不到更新,导致在用户看来界面处于假死的状态,造成了不好的用户体验.所以在大量数 ...

  4. 使用AsyncTask异步更新UI界面及原理分析

    概述: AsyncTask是在Android SDK 1.5之后推出的一个方便编写后台线程与UI线程交互的辅助类.AsyncTask的内部实现是一个线程池,所有提交的异步任务都会在这个线程池中的工作线 ...

  5. Android异步处理系列文章四篇之二 使用AsyncTask异步更新UI界面

    Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面Android异步处理二:使用AsyncTask异步更新UI界面Android异步处理三:Handler+Loope ...

  6. Android 异步加载数据 AsyncTask异步更新界面

    官方文档:     AsyncTask enables proper and easy use of the UI thread. This class allows to perform backg ...

  7. Android异步处理二:使用AsyncTask异步更新UI界面

    在<Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面>中,我们使用Thread+Handler的方式实现了异步更新UI界面,这一篇中,我们介绍一种更为简 ...

  8. Unity3d中制作异步Loading进度条所遇到的问题

    背景 通常游戏的主场景包括的资源较多,这会导致载入场景的时间较长.为了避免这个问题,能够首先载入Loading场景.然后再通过Loading场景来载入主场景. 由于Loading场景包括的资源较少,所 ...

  9. 【Win 10 应用开发】通过数据绑定更新进度条

    实现 INotifyPropertyChanged 接口可以在属性更改后通知数据的使用者,这个相信大伙儿都知道.于是,有朋友会问:对于要实时显示进度的情况,比如更新进度条,能用这个实现吗? 当然是可以 ...

随机推荐

  1. NYoj 部分和问题(深搜经典)

    题目链接: http://acm.nyist.edu.cn/JudgeOnline/problem.php?pid=1058 #include <stdio.h> ], vis[], co ...

  2. crawler_URL编码原理详解

    经常写爬虫的童鞋,难免要处理含有中文的url,大部分时间,都知道url_encode,各个语言也都有支持,今天简单整理下原理,供大家科普 1.特征: 如果URL中含有非ASCII字符的话, 浏览器会对 ...

  3. ExtJS得知--------Ext.Element学习的查询方法(示例)

    详细实例:(实验结果可复制代码后进行演示) Ext.onReady(function(){ Ext.create('Ext.panel.Panel',{//创建一个面板 title:'我的面板' , ...

  4. 【高德地图API】如何打造十月妈咪品牌地图?

    原文:[高德地图API]如何打造十月妈咪品牌地图? 摘要:品牌地图除了地图,商铺标点外,还有微博关注,路线查询等功能. ---------------------------------------- ...

  5. [WPF] 动画Completed事件里获取执行该动画的UI对象

    原文:[WPF] 动画Completed事件里获取执行该动画的UI对象 昨天群里有位童鞋提出如何在动画完成事件Completed里获取到执行该动画的UI对象. WPF里动画的Completed的本身并 ...

  6. tomcat的webapps文件夹下放更新后的项目就訪问不了

    昨天给同事更新完程序,同事说更新后的程序訪问不了.它曾经的程序叫tj52,更新后的程序叫webapp.也就是tomcat的文件夹有两个文件架,一个叫webapp,一个叫tj52.最后另外一同事给了解决 ...

  7. cocos2d-x 3.1.1 学习笔记[13] listen 监听器

    文章出自于  http://blog.csdn.net/zhouyunxuan //创建监听器 auto listen = EventListenerTouchOneByOne::create(); ...

  8. java设计模式之四建造者模式(Builder)

    工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性,其实建造者模式就是前面抽象工厂模式和最后的Test结合起来得到 ...

  9. 算法如功夫——C++ 用递归函数计算n的阶乘n!

    算法如功夫,套路练久了,才干应用自如! 学功夫不能死练,知其所以然,取长补短! #include <iostream.h> int main(int argc, char* argv[]) ...

  10. NET Web开发

    .NET Web开发初学者必知的四个网站   No.1 W3school 链接: http://www.w3school.com.cn/ 预览: 介绍: 全球最大Web前端技术教程网站.内容涵盖从基础 ...