DELPHI盒子
!实时搜索: 盒子论坛 | 注册用户 | 修改信息 | 退出
检举帖 | 全文检索 | 关闭广告 | 捐赠
技术论坛
 用户名
 密  码
自动登陆(30天有效)
忘了密码
≡技术区≡
DELPHI技术
移动应用开发
Web应用开发
数据库专区
报表专区
网络通讯
开源项目
论坛精华贴
≡发布区≡
发布代码
发布控件
文档资料
经典工具
≡事务区≡
网站意见
盒子之家
招聘应聘
信息交换
论坛信息
最新加入: chnifo
今日帖子: 51
在线用户: 19
导航: 论坛 -> DELPHI技术 斑竹:liumazi,sephil  
作者:
男 magiewang (magiewang) ▲▲△△△ -
注册会员
2021/11/22 17:17:45
标题:
求教一个界面显示进度的问题。 浏览:750
加入我的收藏
楼主: 求教各位兄弟!
界面上有个VirtualTree->(frmMain.vstRunTasks),用来显示项目进度,每一行对应一个record记录。
TTaskItem = record
  sTaskName : string;
  sProgressInfo : string;
  iProgress : integer;
end;
PTaskItem = ^TTaskItem;
每个项目对应一个处理线程,在线程中通过更新PTaskItem的sProgressInfo和
iProgress来更新进度。

因为每个线程对应一个独立的PTaskItem,所以更新进度时候直接如下:
procedure TDoRunTask.tSetProgressInfo(sInfo: string);
begin
  //piData is PTaskItem
  if piData.sProgressInfo=sInfo then Exit;
  piData.sProgressInfo := sInfo;
  TThread.Queue(nil,
    procedure
    begin
      //重绘这个节点
      frmMain.vstRunTasks.RepaintNode(piData.nNode);
    end);
end;

问题:
1、因为每个线程只对应一个piData所以更新piData里的数据并没有用临界,这个不知道是否有问题?
2、VirtualTree不会自动刷新数据,因为用TThread.Queue在主线程里RepaintNode重绘这个节点,这种更新操作是否正确,因为有多条线程同时工作并更新界面进度信息,是否会因为多条线程同时RepaintNode导致问题?不知道各位大神平时更新VirtualTree界面进度时候如何操作的?是只更新piData内容,然后通过一个定时器每秒对VirtualTree做刷新还是直接线程里就Repaint了?

谢谢!!!!!
----------------------------------------------
-
作者:
男 ddrfan (若苗瞬) ▲▲▲▲△ -
注册会员
2021/11/22 20:12:35
1楼: 我记得我用多线程,又需要更新界面的时候,是用同步:
线程里面例子大概是这样的:

Synchronize(UpdateUI);
procedure TYourThread.UpdateUI;
begin
  Form1.Caption := 'Updated in a thread';
end;

或者匿名函数
Synchronize(
  procedure
  begin
     Form1.Caption := 'Updated in thread via an anonymous method'
  end
  )
);
----------------------------------------------
Bye bye DDRFAN...
作者:
男 jackalan (nVicen) ★☆☆☆☆ -
盒子活跃会员
2021/11/23 13:10:42
2楼: 一样的,都是同步到主线程去更新UI,至于piData要不要锁,那要看有没有可能同时读写。
----------------------------------------------
简单做人,认真做事。
作者:
男 jackalan (nVicen) ★☆☆☆☆ -
盒子活跃会员
2021/11/23 14:24:29
3楼: 用listview做测试,50个线程对应50个ITEM,只要不做太复杂的重绘,是没问题的,如果你要重绘进度条那些,线程频繁更新界面,还是有感觉会卡的。
----------------------------------------------
简单做人,认真做事。
作者:
男 gmxyb (gmxyb) ▲▲▲▲▲ -
注册会员
2021/11/24 19:14:01
4楼: 建议“只更新piData内容,然后通过一个定时器每秒对VirtualTree做刷新”

这样做VT刷新的频率可控,效率稍高一些。如果线程更新过于频繁,界面上也没必要刷太快。

不过piData的读写还是要加锁,线程修改 piData, VT要去读 piData,不加锁会有概率出问题的,你的piData中有 string 字段,多线程读写容易出问题,如果全部是 Integer 之类的简单类型,也可以不加。

每次读写 pidData时间很短,可以直接用一个全局的临界段锁,不用每个 piData 一个锁。
----------------------------------------------
-
作者:
男 sail2000 (小帆工作室) ★☆☆☆☆ -
盒子活跃会员
2021/11/25 10:13:22
5楼: 开一个单独的线程,去读数据,一次性读取所有其他线程给出的进度再更新UI即可,不要每个写数据的线程都去访问UI,这样必须要加锁,造成效率十分低下。
----------------------------------------------
delphi 是兴趣,和工作无关,即使它倒闭。
又不靠它 delphi 吃饭,怕甚?
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2021/11/25 11:49:56
6楼: 5楼说得有道理。

但是,那个单独的线程,因为它是一个线程,它要刷新界面,就需要去做和主线程的同步。

其背后的机理,其实就是发个消息给主线程,让主线程去做刷新界面的操作。

另外,假设直接丢一个 Timer 上去,OnTimer 去刷新界面,因为 OnTimer 是主线程操作的,因此就没有和主线程同步到要求了。

另外,虽然刷新界面的代码,对数据是只读不写,但因为数据本身是在被其它线程写,所以读写之间,还是加上锁比较安全。当然,一个只读,一个写,不加锁,出问题的几率不大。
----------------------------------------------
-
作者:
男 jackalan (nVicen) ★☆☆☆☆ -
盒子活跃会员
2021/11/25 13:58:09
7楼: 4楼说的有理,下面的代码多开几条,主界面就卡死,然后程序无响应了,
原因就是太过频繁的去刷新主界面,Synchronize是在主线程执行的,插入
主线程后,当前线程挂起,执行完在跳回继续执行,所以导致无响应。

建议用定时器每秒刷新一次,这样测试的效果还不错,至于需不需要用个线
程单独去刷新数据,我觉得没必要。

for var i := 1 to 10000 do
begin
  piData.sProgressInfo := Format('Now Process -> %d', [i]);
  piData.iProgress := Round(i/100);
  TThread.Queue(nil,
    procedure
    begin
      frmMain.vstRunTasks.RepaintNode(piData.nNode);
    end);
end;
----------------------------------------------
简单做人,认真做事。
作者:
男 bluestorm8 (bluestorm) ▲▲△△△ -
注册会员
2021/11/25 15:41:21
8楼: 【Delphi】以高性能方式把线程信息显示在界面上的一个通用小框架
https://blog.csdn.net/BlueStorm/article/details/113107746
----------------------------------------------
-
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2021/11/25 16:40:55
9楼: 如果有很多个线程在更新很多个数据,假设有100个线程。假设每个线程更新数据的频率是1秒更新 100 次。那么,如果你每个线程更新完数据之后就马上更新界面,那就是每秒钟更新界面:一万次。

当然程序会很忙。要知道更新界面这个事情是非常消耗 CPU 资源的。程序很忙,就卡死了,反应慢了。

界面更新的及时性?一秒一次,满足多数情况。一秒三次,绝大多数情况都够用。如果是视频,一秒30次也够用了。

有了这个概念,才好确定目标。确定了目标,才知道自己的程序该怎么做。假设目标是 1 秒一次,那你搞个线程 1 秒循环去读一次那 100 个数据,读出来的值去刷新界面。最笨的办法就是搞个 Timer,一秒一次。

撇开对数据的读写加锁,线程对界面的更新需要同步到主界面等不说,然后,这里有个有意思的事情来了:

之前,我的代码是硬刷界面,比如写成:Label1.Caption := MyObj.MyAge.

但这样写代码,代码比较乱,数据多了东西多了,也容易漏掉。

基于所谓的 MVVM 之类的原则,这里其实用 Delphi 的 LiveBinding比较好玩了,设计期拉拉线,数据和界面严格隔离开,需要写的代码就很少了。
----------------------------------------------
-
作者:
男 tiez (骑牛夜旅) ★☆☆☆☆ -
普通会员
2021/11/26 15:51:42
10楼: 有段时间没写程序了,对界面控件的印象慢慢变得模糊。

你考虑到在线程里取得数据刷到界面上,有两点是明确的:1、你知道最终的刷是要同步到主线程刷的。2、内心对刷新频次和资源消耗已有一定预期。

那这种情况形成的原因一般就是这两步超过了你的设想。

从代码看你程序相当简单,就是用了主线程排队执行仅有的一条RepaintNode了。从这点上看提升的可能不大,但如执行频次非常高还是有可能堵死的。
可改为如下方式,你目的是想把一个string更新到界面上。改成如下方式:
  1、自定义一个消息。
  2、PostThreadMessage带串到主线程(自己上网查一下跨线程传递字符串学习一下)
  3、主线程消息内比较串并刷界面(注意消息处理方式不要造成内存泄漏)。
如对跨线程消息带串没有把握,也可以用定义全局string,加锁读写发消息。主线程收消息或在空闲消息中刷了比较的方法。

  但是,我认为上面这个方法很可能对你没用,只是帮你排除是方式问题的疑虑。实际上很可能是刷新资源消耗超出你预期造成的这个问题。我前面说过了我一段时间没做程序了,对RepaintNode在你程序中刷新的资源消耗情况也不了解。但你要注意一点,树控件的刷新在delphi中往往是很消耗资源的,你可以看下所用控件是否有beginupdate和endupdate方法,如有可以调用之后再改,再看看是不是树控件的刷新时关联的刷新动作太多造成卡顿。
----------------------------------------------
-
作者:
男 magiewang (magiewang) ▲▲△△△ -
注册会员
2021/11/28 16:56:46
11楼: 首先感谢各位大大精彩回答。

目前改用TIMER方式1秒刷新一次,除了感觉不够实时外,也还能接受,也没发现无响应卡死的问题了。

postmessage的方式我之前用过,开的线程多了以后由于1秒内太多POSTMESSAGE到消息队列,最后尽然和SYN一样,导致了主界面卡死,然后程序无响应。

我感觉VC写的程序,大量线程并发刷新界面效果很好,也没有导致无响应的问题,但我本身不懂VC,那个代码还是以前的师兄写的,现在VCL下,刷新主界面除了同步、消息、TIMER外,应该也没有更好的方式了吧?
----------------------------------------------
-
作者:
男 bluestorm8 (bluestorm) ▲▲△△△ -
注册会员
2021/11/29 9:56:22
12楼: Syncloniz最大问题是子线程要等待和主线程同步,这不但会导致信息显示慢,还会拖慢线程运行。
PostMessage没有这个线程同步的问题,效率高很多。

我以前写过一个显示大量信息的程序,刚开始的时候也是采用Synclonize方式,但发现这种方式很慢,采用PostMessage后信息显示非常快。
----------------------------------------------
-
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2021/11/29 10:02:09
13楼: 11 楼,不管 VC 还是 DELPHI,原理还是一样的。如果你让 VC 的程序一秒钟刷新界面一万次,它也一样受不了。

如果一秒一次你觉得慢,那就3次好了。其实再快也没有意义。人眼的反应速度就那么多,高于低于 300 毫秒的速度你基本上感觉不出来了。当然,视频就要30毫秒了,因为视频的目的就是让你感觉不出来,利用视觉暂留原理让你感觉多张间隔显示的图片是连续的动作。
----------------------------------------------
-
作者:
男 lsz100 (lsz) ★☆☆☆☆ -
盒子活跃会员
2021/11/29 12:05:04
14楼: 哈哈 这里还有一个问题  就是刷新界面快慢要有等级的 比如你本人的无人机操作界面 哪必需是实时刷新  如显视其他无人机状态时可以每秒一次
----------------------------------------------
我为人人为我
信息
登陆以后才能回复
Copyright © 2CCC.Com 盒子论坛 v2.1 版权所有 页面执行15.625毫秒 RSS