C# BackgroundWorker 和 ProgressBar 问题
C# BackgroundWorker and ProgressBar issue
我有两个文件。一个包含 DataProgressBar 类型的变量并在加载其自己的组件之前调用 startAsyncWorker() 方法。另一个是下面显示的 DataProgressBar。当我 运行 我的程序并在其他文件中调用 startAsyncWorker() 时,行为
EXPECTED: small window 显示进度条,随着 WorkerDatabaseInsertion 中工作的执行从 0 加载到 100。然后当我的第一个包含 DataProgressBar 的 class 文件完成后,将继续执行下一条指令。
EXPERIENCED: 小 window 显示进度条,未对 UI 进行任何更改,线程似乎冻结,因为没有输出或证据处理的显示。我关闭 window,调用文件恢复。
public partial class DataProgressBar : Window
{
private BackgroundWorker bgw;
private String _path;
private LFPReader _lfp;
private Access db;
public DataProgressBar(String p, LFPReader reader, Access database)
{
InitializeComponent();
/* Set private class level variables */
_path = p;
_lfp = reader;
db = database;
db.open();
/* Set up worker and progressbar */
bgw = new BackgroundWorker();
SetUpWorker(bgw);
progressbar.Maximum = 100;
progressbar.Minimum = 0;
progressbar.Value = 0;
}
public void startAsyncWorker()
{
if(bgw.IsBusy != true)
{
bgw.RunWorkerAsync();
}
}
/// <summary>
/// This methods exists for completeness, but we will
/// probably not need to directly cancel the worker from here.
/// --Kurtpr
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void cancelAsyncWorker(object sender, EventArgs e)
{
if (bgw.WorkerSupportsCancellation == true)
{
bgw.CancelAsync();
}
}
private void SetUpWorker(BackgroundWorker worker)
{
worker.WorkerReportsProgress = true; // we need this in order to update the UI
worker.WorkerSupportsCancellation = true; // Not sure why, but we may need this to cancel
worker.DoWork += new DoWorkEventHandler(WorkerDatabaseInsertion);
worker.ProgressChanged += new ProgressChangedEventHandler(WorkerProgressChanged);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(WorkerBurnNotice);
}
private void WorkerDatabaseInsertion(object sender, DoWorkEventArgs e) {
BackgroundWorker worker = sender as BackgroundWorker;
ImageInfo ImageData = new ImageDB.ImageInfo();
double size = _lfp.GetImageData().ToArray().Length;
int index = 0;
//_path is setup in loadAsLFP() before this call.
foreach (var image in _lfp.GetImageData())
{
index++;
if (worker.CancellationPending == true)
{
e.Cancel = true;
break;
}
image.FullPath = Path.Combine(_path, image.FullPath);
ImageData.Add(new ImageDB(image));
db.insertImage(new ImageDB(image));
worker.ReportProgress((int)(index/size));
}
}
private void WorkerProgressChanged(object sender, ProgressChangedEventArgs e)
{
Console.WriteLine("Hello");
progressbar.Value = e.ProgressPercentage;
}
private void WorkerBurnNotice(object sender, RunWorkerCompletedEventArgs e)
{
db.close();
}
}
我怀疑我对 BackgroundWorker 的一个基本方面是错误的。我的逻辑错误在哪里?
编辑 下面是调用和创建 DataProgressBar 对象的代码。
private void createDb(string filePath, LFPReader lfp)
{
//Set up LFP related objects.
ImageData = new ImageDB.ImageInfo();
//create database file.
filePath = filePath.Replace(".lfp", ".mdb").Replace(".LFP", ".mdb");
_lfpName = filePath; // update to use the database file
Access db = new Access(filePath.Replace(".lfp", ".mdb"));
db.createDatabase();
//used for calculating progress of creating this new database.
var progressbar = new DataProgressBar(_path, lfp, db);
progressbar.ShowDialog();
progressbar.startAsyncWorker();
CurImage = ImageData.First().FullPath;
//Make sure that data context is set for these images.
DataContext = ImageData;
}
我认为你的进度计算逻辑有问题。
worker.ReportProgress((int)(index/size));
这一行会一直报告进度为0。所以,你的进度条会一直卡在0的位置。
而是使用以下以百分比形式报告进度。
worker.ReportProgress((int)(index*100/size));
更新:
您共享的代码似乎是正确的。
我认为问题在于您实现进度条的方式。
我想你正在从主线程调用 startAsyncWorker 方法,如下所示;
var dpb = new DataProgressBar(p, reader, database);
dpb.startAsyncWorker();
调用以上两行后,你的主线程应该空闲了。
这意味着,以下代码将导致您的进度条冻结 50 秒,因为即使您的 DoWork 运行 完美无缺,UI 也不会更新,因为主线程不空闲。
var dpb = new DataProgressBar(p, reader, database);
dpb.startAsyncWorker();
Thread.Sleep(50000); //Main thread is busy for 50 seconds
更新二:
真正的问题在于以下几行;
var progressbar = new DataProgressBar(_path, lfp, db);
progressbar.ShowDialog();
progressbar.startAsyncWorker();
实际上 ShowDialog()
方法将 DataProgressBar
显示为模态对话框。这意味着,除非您关闭该对话框,否则控件不会转到下一行。
您的问题应使用以下代码解决;
var progressbar = new DataProgressBar(_path, lfp, db);
progressbar.startAsyncWorker();
progressbar.ShowDialog();
它将首先启动您的后台工作程序,然后将显示 DataProgressBar 对话框。
我有两个文件。一个包含 DataProgressBar 类型的变量并在加载其自己的组件之前调用 startAsyncWorker() 方法。另一个是下面显示的 DataProgressBar。当我 运行 我的程序并在其他文件中调用 startAsyncWorker() 时,行为
EXPECTED: small window 显示进度条,随着 WorkerDatabaseInsertion 中工作的执行从 0 加载到 100。然后当我的第一个包含 DataProgressBar 的 class 文件完成后,将继续执行下一条指令。
EXPERIENCED: 小 window 显示进度条,未对 UI 进行任何更改,线程似乎冻结,因为没有输出或证据处理的显示。我关闭 window,调用文件恢复。
public partial class DataProgressBar : Window
{
private BackgroundWorker bgw;
private String _path;
private LFPReader _lfp;
private Access db;
public DataProgressBar(String p, LFPReader reader, Access database)
{
InitializeComponent();
/* Set private class level variables */
_path = p;
_lfp = reader;
db = database;
db.open();
/* Set up worker and progressbar */
bgw = new BackgroundWorker();
SetUpWorker(bgw);
progressbar.Maximum = 100;
progressbar.Minimum = 0;
progressbar.Value = 0;
}
public void startAsyncWorker()
{
if(bgw.IsBusy != true)
{
bgw.RunWorkerAsync();
}
}
/// <summary>
/// This methods exists for completeness, but we will
/// probably not need to directly cancel the worker from here.
/// --Kurtpr
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void cancelAsyncWorker(object sender, EventArgs e)
{
if (bgw.WorkerSupportsCancellation == true)
{
bgw.CancelAsync();
}
}
private void SetUpWorker(BackgroundWorker worker)
{
worker.WorkerReportsProgress = true; // we need this in order to update the UI
worker.WorkerSupportsCancellation = true; // Not sure why, but we may need this to cancel
worker.DoWork += new DoWorkEventHandler(WorkerDatabaseInsertion);
worker.ProgressChanged += new ProgressChangedEventHandler(WorkerProgressChanged);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(WorkerBurnNotice);
}
private void WorkerDatabaseInsertion(object sender, DoWorkEventArgs e) {
BackgroundWorker worker = sender as BackgroundWorker;
ImageInfo ImageData = new ImageDB.ImageInfo();
double size = _lfp.GetImageData().ToArray().Length;
int index = 0;
//_path is setup in loadAsLFP() before this call.
foreach (var image in _lfp.GetImageData())
{
index++;
if (worker.CancellationPending == true)
{
e.Cancel = true;
break;
}
image.FullPath = Path.Combine(_path, image.FullPath);
ImageData.Add(new ImageDB(image));
db.insertImage(new ImageDB(image));
worker.ReportProgress((int)(index/size));
}
}
private void WorkerProgressChanged(object sender, ProgressChangedEventArgs e)
{
Console.WriteLine("Hello");
progressbar.Value = e.ProgressPercentage;
}
private void WorkerBurnNotice(object sender, RunWorkerCompletedEventArgs e)
{
db.close();
}
}
我怀疑我对 BackgroundWorker 的一个基本方面是错误的。我的逻辑错误在哪里?
编辑 下面是调用和创建 DataProgressBar 对象的代码。
private void createDb(string filePath, LFPReader lfp)
{
//Set up LFP related objects.
ImageData = new ImageDB.ImageInfo();
//create database file.
filePath = filePath.Replace(".lfp", ".mdb").Replace(".LFP", ".mdb");
_lfpName = filePath; // update to use the database file
Access db = new Access(filePath.Replace(".lfp", ".mdb"));
db.createDatabase();
//used for calculating progress of creating this new database.
var progressbar = new DataProgressBar(_path, lfp, db);
progressbar.ShowDialog();
progressbar.startAsyncWorker();
CurImage = ImageData.First().FullPath;
//Make sure that data context is set for these images.
DataContext = ImageData;
}
我认为你的进度计算逻辑有问题。
worker.ReportProgress((int)(index/size));
这一行会一直报告进度为0。所以,你的进度条会一直卡在0的位置。
而是使用以下以百分比形式报告进度。
worker.ReportProgress((int)(index*100/size));
更新: 您共享的代码似乎是正确的。 我认为问题在于您实现进度条的方式。
我想你正在从主线程调用 startAsyncWorker 方法,如下所示;
var dpb = new DataProgressBar(p, reader, database);
dpb.startAsyncWorker();
调用以上两行后,你的主线程应该空闲了。
这意味着,以下代码将导致您的进度条冻结 50 秒,因为即使您的 DoWork 运行 完美无缺,UI 也不会更新,因为主线程不空闲。
var dpb = new DataProgressBar(p, reader, database);
dpb.startAsyncWorker();
Thread.Sleep(50000); //Main thread is busy for 50 seconds
更新二:
真正的问题在于以下几行;
var progressbar = new DataProgressBar(_path, lfp, db);
progressbar.ShowDialog();
progressbar.startAsyncWorker();
实际上 ShowDialog()
方法将 DataProgressBar
显示为模态对话框。这意味着,除非您关闭该对话框,否则控件不会转到下一行。
您的问题应使用以下代码解决;
var progressbar = new DataProgressBar(_path, lfp, db);
progressbar.startAsyncWorker();
progressbar.ShowDialog();
它将首先启动您的后台工作程序,然后将显示 DataProgressBar 对话框。