C# 多背景辅助角色
本文关键字:角色 背景 | 更新日期: 2023-09-27 18:34:49
我正在尝试设置多个BackgroundWorker
来做工作,不忙的时候开始做下一点工作。我似乎无法让它们正常工作。我有以下代码。
当我将FilesToProcess
设置为等于或小于MaxThreads
时,它可以完美运行,尽管如果我将其提高,应用程序会冻结。
我确信这是一件简单的事情,但我就是看不到它。
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
namespace bgwtest
{
public partial class Form1 : Form
{
private const int MaxThreads = 20;
private const int FilesToProcess = 21;
private BackgroundWorker[] threadArray = new BackgroundWorker[MaxThreads];
public Form1()
{
InitializeComponent();
}
private void Form1Load(object sender, EventArgs e)
{
InitializeBackgoundWorkers();
}
private void InitializeBackgoundWorkers()
{
for (var f = 0; f < MaxThreads; f++)
{
threadArray[f] = new BackgroundWorker();
threadArray[f].DoWork += new DoWorkEventHandler(BackgroundWorkerFilesDoWork);
threadArray[f].RunWorkerCompleted += new RunWorkerCompletedEventHandler(BackgroundWorkerFilesRunWorkerCompleted);
threadArray[f].WorkerReportsProgress = true;
threadArray[f].WorkerSupportsCancellation = true;
}
}
private void button1_Click(object sender, EventArgs e)
{
for (var f = 0; f < FilesToProcess; f++)
{
var fileProcessed = false;
while (!fileProcessed)
{
for (var threadNum = 0; threadNum < MaxThreads; threadNum++)
{
if (!threadArray[threadNum].IsBusy)
{
Console.WriteLine("Starting Thread: {0}", threadNum);
threadArray[threadNum].RunWorkerAsync(f);
fileProcessed = true;
break;
}
}
if (!fileProcessed)
{
Thread.Sleep(50);
}
}
}
}
private void BackgroundWorkerFilesDoWork(object sender, DoWorkEventArgs e)
{
ProcessFile((int)e.Argument);
e.Result = (int)e.Argument;
}
private static void ProcessFile(int file)
{
Console.WriteLine("Processing File: {0}", file);
}
private void BackgroundWorkerFilesRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
MessageBox.Show(e.Error.Message);
}
Console.WriteLine("Processed File: {0}", (int)e.Result);
}
}
}
问题似乎是你的工人永远不会完成。 为什么会这样,我不确定;这与运行它们的方法(和线程(本身并未完成这一事实有关。 我能够通过创建另一个工作线程将文件分配给工作线程数组来解决问题:
private BackgroundWorker assignmentWorker;
private void InitializeBackgoundWorkers() {
assignmentWorker = new BackgroundWorker();
assignmentWorker.DoWork += AssignmentWorkerOnDoWork;
// ...
}
private void AssignmentWorkerOnDoWork( object sender, DoWorkEventArgs doWorkEventArgs ) {
for( var f = 0; f < FilesToProcess; f++ ) {
var fileProcessed = false;
while( !fileProcessed ) {
for( var threadNum = 0; threadNum < MaxThreads; threadNum++ ) {
if( !threadArray[threadNum].IsBusy ) {
Console.WriteLine( "Starting Thread: {0}", threadNum );
threadArray[threadNum].RunWorkerAsync( f );
fileProcessed = true;
break;
}
}
if( !fileProcessed ) {
Thread.Sleep( 50 );
break;
}
}
}
}
private void button1_Click( object sender, EventArgs e ) {
assignmentWorker.RunWorkerAsync();
}
我对这个答案不满意,因为我不知道为什么,确切地说,它没有像你最初设计的那样工作。 也许其他人可以回答这个问题...? 至少这会给你一个工作版本。
编辑:您的原始版本不起作用,因为BackgroundWorkerFilesRunWorkerCompleted
与button1_Click
(UI线程(在同一线程上运行。 由于您没有释放 UI 线程,因此该线程永远不会被标记为完成。
在完成 Ethan Brown 的回答时,问题实际上来自这样一个事实,即您在 UI 线程中运行了一个永远繁忙的循环。
如果filesToProcess
小于最大线程数,循环会将每个文件分配给一个线程并退出。那里没有问题。
但是,如果您的文件多于线程,请查看在所有线程已在运行时尝试处理文件时会发生什么:
- 您检查是否所有线程都繁忙,是的。
- 您通过执行线程睡眠来等待。
问题是,当后台工作线程完成时,它会将消息发布到 UI 线程消息队列中。您的 UI 线程实际上正忙于检查您的线程并等待。
要解决这个问题:
- 要么你把循环放在另一个线程中(作为伊桑布朗的解决方案(
- 例如,您可以在 Thread.Sleep(( 之前或之后添加
Application.DoEvents()
,以强制您的 UI 线程处理消息。这不是真正的好做法,但是很好地说明了这里的问题。