我正在尝试(再次)在我的应用中实现Backgroundworker,以便UI能够响应。用户选择要处理的文件,最终在网格上。处理开始时就像这样:
for (int i = 0; i<Grid.Rows.Count; i++)
{
Worker work = new Worker();
//set up data for processing based on the current row in the grid
bw.RunWorkerAsync(work);
addStatusToGrid();
//clean up; indicate work on this file is done
work=null;
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
Worker work = (Worker)e.Argument;
if (work.doTheWork() == false)
{
//indicate failure
}
else
{
//indicate success
}
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//not sure what to do here
}
但是,只要调用bw.RunWorkerAsync(),应用程序就会立即转到for循环中的下一行,然后再次启动相同的进程。现在,我有点想要这个,但是我收到一条错误消息,“这个后台工作者当前很忙,不能同时运行多个任务”。如果我只处理一个项目,立即调用addStatusToGrid(),当然没有状态可以添加,因为工作没有完成。
几个问题:如果我可以使用此机制一次启动多个处理会话,那将是一件好事。但是如何防止立即调用addStatusToGrid()?
现在,UI确实得到了很多更新。我有一个进度条,它经常显示更新。我不能用取消按钮取消操作,因为UI线程正忙。
答案 0 :(得分:0)
从您的问题看来,网格中有多行需要处理。现在,后台工作程序在另一个线程中执行该方法,从而释放UI以执行其他操作。 (IE状态更新)。由于这是在另一个线程上执行的,因此UI可以正常继续处理。 UI线程不会等待BackgroundWorker完成。如果它确实等待,那么使用后台工作程序是没有用的。此外,BackgroundWorker必须在开始另一个操作之前完成操作(因此您的错误消息)。
现在很难理解你在work.doTheWork()
做了什么,但我怀疑你想在这里采取另一种方法。
在我们开始解决问题之前,你必须首先理解的是取消后台工作者实际上并没有取消线程,而是为你的代码提供一个“标志”来检查BackgroundWorker是否正在等待计算({{3 }})。
前进。由于Grid位于UI线程上,您需要了解我们要捕获的网格中的数据并将其传递给BackgroundWorker上下文。正如本提供的那样,我将做一个非常基本的例子。现在,您可以运行多个后台工作程序来单独处理每一行,从而在Grid中每行产生1个worker。对于您的要求,这可能是理想的,但对于较大的网格,您实际上每行创建一个线程进行处理,并且在具有数百行的网格中,这可能是灾难性的。
因此,您可以创建类似下面的方法。我已经对代码进行了评论,以帮助您完成它。基本上,这显示了取消工作人员,报告进度并单独遍历每个行项目的能力。我还在worker中添加了一些基本类。 [注意这只是演示代码]
class DoWorkTester
{
int currentIndex = 0;
GridRow[] rows;
public void ExecuteWorkers()
{
GridRow rowA = new GridRow
{
PropertyA = "abc",
PropertyB = "def"
};
GridRow rowB = new GridRow
{
PropertyA = "123",
PropertyB = "456"
};
GridRow rowC = new GridRow
{
PropertyA = "xyz",
PropertyB = "789"
};
rows = new GridRow[] { rowA, rowB, rowC };
currentIndex = 0;
runWorkers();
}
BackgroundWorker worker;
void runWorkers()
{
//done all rows
if (currentIndex >= rows.Length - 1)
return;
//is the worker busy?
if (worker != null && worker.IsBusy)
{
//TODO: Trying to execute on a running worker.
return;
}
//create a new worker
worker = new BackgroundWorker();
worker.WorkerSupportsCancellation = true;
worker.WorkerReportsProgress = true;
worker.ProgressChanged += (o, e) =>
{
//TODO: Update the UI that the progress has changed
};
worker.DoWork += (o, e) =>
{
if (currentIndex >= rows.Length - 1)
{
//indicate done
e.Result = new WorkResult
{
Message = "",
Status = WorkerResultStatus.DONE
};
return;
}
//check if the worker is cancelling
else if (worker.CancellationPending)
{
e.Result = WorkResult.Cancelled;
return;
}
currentIndex++;
//report the progress to the UI thread.
worker.ReportProgress(currentIndex);
//TODO: Execute your logic.
if (worker.CancellationPending)
{
e.Result = WorkResult.Cancelled;
return;
}
e.Result = WorkResult.Completed;
};
worker.RunWorkerCompleted += (o, e) =>
{
var result = e.Result as WorkResult;
if (result == null || result.Status != WorkerResultStatus.DONE)
{
//TODO: Code for cancelled \ failed results
worker.Dispose();
worker = null;
return;
}
//Re-call the run workers thread
runWorkers();
};
worker.RunWorkerAsync(rows[currentIndex]);
}
/// <summary>
/// cancel the worker
/// </summary>
void cancelWorker()
{
//is the worker set to an instance and is it busy?
if (worker != null && worker.IsBusy)
worker.CancelAsync();
}
}
enum WorkerResultStatus
{
DONE,
CANCELLED,
FAILED
}
class WorkResult
{
public string Message { get; set; }
public WorkerResultStatus Status { get; set; }
public static WorkResult Completed
{
get
{
return new WorkResult
{
Status = WorkerResultStatus.DONE,
Message = ""
};
}
}
public static WorkResult Cancelled
{
get
{
return new WorkResult
{
Message = "Cancelled by user",
Status = WorkerResultStatus.CANCELLED
};
}
}
}
class GridRow
{
public string PropertyA { get; set; }
public string PropertyB { get; set; }
}
现在,如果您想一次处理多行,则必须调整代码以使用某种工作池或将所有行数据传递给第一个后台工作程序,从而删除递归。
干杯。