C# BackgroundWorker冻结了我的用户界面,BeginInvoke似乎以极快的速度运行
我必须完成一项看似简单的任务:1)列出目录(及其子目录)中的所有文件,2)在多行文本框中显示所有文件,然后3)在每个文件中执行一些操作。由于两个问题,我陷入了困境,以下是我的问题:C# BackgroundWorker冻结了我的用户界面,BeginInvoke似乎以极快的速度运行,c#,multithreading,winforms,backgroundworker,C#,Multithreading,Winforms,Backgroundworker,我必须完成一项看似简单的任务:1)列出目录(及其子目录)中的所有文件,2)在多行文本框中显示所有文件,然后3)在每个文件中执行一些操作。由于两个问题,我陷入了困境,以下是我的问题: Form1.cs是我管理UI并启动BackgroundWorker 它运行Logic.cs的主要功能 DependencyMapper.cs是。。。在这里,我执行文件夹/文件操作(在Fetch())并调用Form1方法,该方法使用BeginInvoke将每一行(当前文件的名称)填充到Form1的文本框中 少说话多代码
Form1.cs
是我管理UI并启动BackgroundWorker
它运行Logic.cs
的主要功能DependencyMapper.cs
是。。。在这里,我执行文件夹/文件操作(在Fetch()
)并调用Form1
方法,该方法使用BeginInvoke
将每一行(当前文件的名称)填充到Form1的
文本框中Form1.cs
public partial class Form1 : Form
{
public DependencyMapper dep;
BackgroundWorker bwDep;
public Form1()
{
// I read here in SO to try put the BW stuff here don't know why, but hasn't helped.
InitializeComponent();
bwDep = new BackgroundWorker();
bwDep.DoWork += bwDep_DoWork;
bwDep.RunWorkerCompleted += bwDep_RunWorkerCompleted;
}
private void button1_Click(object sender, EventArgs e)
{
bwDep.RunWorkerAsync();
}
void bwDep_DoWork(object sender, DoWorkEventArgs e)
{
dep.Fetch(extensions);
}
public void SendBack(string msg) // To receive Fetch()s progress
{
textBox2.BeginInvoke(new Action(() =>
{
textBox2.Text += msg + "\r\n";
textBox2.SelectionStart = textBox2.Text.Length;
textBox2.ScrollToCaret();
}));
}
}
public class DependencyMapper
{
private Form1 form;
public DependencyMapper(Form1 form1)
{
this.form = form1;
}
public void Fetch()
{
DirectoryInfo folder = new DirectoryInfo(form.Texto1);
FileInfo[] files = folder.GetFiles("*.*", SearchOption.AllDirectories);
for (int i = 0; i < files.Length; i++)
{
form.SendBack(files[i].FullName); // Kind of talking back to the UI through form's reference and SendBack method which uses BeginInvoke.
}
}
}
dependencMapper.cs
public partial class Form1 : Form
{
public DependencyMapper dep;
BackgroundWorker bwDep;
public Form1()
{
// I read here in SO to try put the BW stuff here don't know why, but hasn't helped.
InitializeComponent();
bwDep = new BackgroundWorker();
bwDep.DoWork += bwDep_DoWork;
bwDep.RunWorkerCompleted += bwDep_RunWorkerCompleted;
}
private void button1_Click(object sender, EventArgs e)
{
bwDep.RunWorkerAsync();
}
void bwDep_DoWork(object sender, DoWorkEventArgs e)
{
dep.Fetch(extensions);
}
public void SendBack(string msg) // To receive Fetch()s progress
{
textBox2.BeginInvoke(new Action(() =>
{
textBox2.Text += msg + "\r\n";
textBox2.SelectionStart = textBox2.Text.Length;
textBox2.ScrollToCaret();
}));
}
}
public class DependencyMapper
{
private Form1 form;
public DependencyMapper(Form1 form1)
{
this.form = form1;
}
public void Fetch()
{
DirectoryInfo folder = new DirectoryInfo(form.Texto1);
FileInfo[] files = folder.GetFiles("*.*", SearchOption.AllDirectories);
for (int i = 0; i < files.Length; i++)
{
form.SendBack(files[i].FullName); // Kind of talking back to the UI through form's reference and SendBack method which uses BeginInvoke.
}
}
}
公共类依赖项apper
{
私人表格1表格;
公共从属上诉人(表格1表格1)
{
this.form=form1;
}
公共空取()
{
DirectoryInfo文件夹=新的DirectoryInfo(form.Texto1);
FileInfo[]files=folder.GetFiles(“*.*”,SearchOption.AllDirectories);
for(int i=0;i
那么,我的应用程序能用吗?是的,但有两个大问题我无法解决:
BackgroundWorker
?)。不完全是因为文本框一个接一个地添加每个文件,但就像它应该做的那样,但我不能移动窗口或单击任何按钮BackgroundWorker
之前,我使用的是Thread
:UI根本没有冻结,但文本框填充率同样缓慢。这就是为什么我决定与BackgroundWorker
合作,这只会带来问题1
谢谢。您正在使用UI线程,生成的结果比UI显示的结果快得多。一开始可能还算不错,但随着时间的推移,情况会越来越糟。您强制文本框重新分配内存,以便为附加的字符串找到空间,复制原始文本并附加新文本。一旦文本框包含一兆字节的文本,这将严重地开始拖拽。与System.String类存在的问题相同。TextBox没有任何类似的功能
通常的症状是UI线程将开始燃烧100%的内核。在某个关键点,它会变得完全紧张,不再重新绘制UI,对输入没有反应。因为每次更新Text属性时,它都会调用另一个委托。没有时间做优先级较低的工作,比如绘制和清空消息队列。调用队列本身开始落后,收集越来越多等待处理的调用请求。在极端情况下,这将导致程序崩溃,并导致OutOfMemoryException,但这需要很长时间。即使后台工作人员完成了,UI线程也会在这之后忙碌很长一段时间,试图处理积压工作
UI通常是不起作用的,甚至在它碰到墙之前。用户只是盯着一个令人眼花缭乱的快速滚动文本框,不允许实际阅读任何内容。使用文本框本身会适得其反,用户编辑列表没有任何意义
显然,你需要以不同的方式来做。在调用之前,至少在StringBuilder中收集一大堆文件。这将给文本框一个机会,让它不得不经常重新分配。每50毫秒调用一次以上是没有意义的,这大约是人眼看到变化而不变成模糊的速度。大多数这样做的程序只是显示一个迭代文件的示例,以便用户对进度有一些反馈。在这种情况下,在StringBuilder中收集所有数据并在文件迭代完成之前不更新文本框是完全合理的。在表单初始化之前,尝试放置一个线程(后台)来准备文件夹和文件列表。考虑在表单构造或全局级别上传递列表的方法。
dep.Fetch(extensions)代码>什么是dep?什么是扩展?带参数的Fetch函数在哪里?请张贴准确的密码。考虑只将最终Relt[Cord]String []/Calp>传递给窗体,如果这是适用的。当需要在后台做某件事时,线程很方便。实际上,您并没有从中受益,因为您一直在与UI对话。真正的好处是完全在后台执行繁重的操作,并在完成后将结果传递给UI。@请记住,使用当前进度更新UI线程没有什么错。这就是backgroundworker::ReportProgress()
的目的。出于某种原因,他没有使用它,但在他的代码(至少是发布的代码)中,UI线程上没有繁重的工作。@AviTurner在我看来,这肯定会出错:GetFiles(“**”),SearchOption.AllDirectories)
如果输入C:\
,则这可能需要很长时间。每个文件名都被发送回
到UI。我放弃了更新用户的进度(我仍然在编写我想做的实际逻辑),而是将结果打包发送回。但这是一个令人难以置信的回答,给了我一个广阔的视角,谢谢。