我使用多个BackgroundWorker
控件来运行某些任务作为多线程。但是我发现当使用4个以上的BackgroundWoker时,从第4个前向延迟的那个延迟超过第二个,从而在调用RunWorkerAsync
时实际执行。
可以帮助我如何立即启动所有背景工作者?
class TaskLog
{
public int task_id;
public DateTime call_time;
public DateTime start_time;
public DateTime end_time;
}
BackgroundWorker[] bws = new BackgroundWorker[18];
int[] tasks = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
Queue<TaskLog> queueTask;
TaskLog[] records;
int task_complete = 999;
private void button1_Click(object sender, EventArgs e)
{
if (task_complete < tasks.Length) return;
task_complete = 0;
records = tasks.Select(t => new TaskLog { task_id = t }).ToArray();
queueTask = new Queue<TaskLog>(records);
for (int i = 0; i < bws.Length && queueTask.Count > 0; ++i)
{
bws[i] = new BackgroundWorker();
bws[i].DoWork += new DoWorkEventHandler(download_vid_work);
bws[i].RunWorkerCompleted += new RunWorkerCompletedEventHandler(download_vid_complete);
var x = queueTask.Dequeue();
x.call_time = DateTime.Now;
bws[i].RunWorkerAsync(x);
//Debug.WriteLine("start " + x.task_id);
}
}
void download_vid_work(object sender, DoWorkEventArgs e)
{
var record = (TaskLog)e.Argument;
record.start_time = DateTime.Now;
//Debug.WriteLine("actually start " + record.task_id);
Thread.Sleep(10000); // 10s
e.Result = record;
}
void download_vid_complete(object sender, RunWorkerCompletedEventArgs e)
{
var record = (TaskLog)e.Result;
record.end_time = DateTime.Now;
//Debug.WriteLine("complete " + item.ToString());
++task_complete;
if (task_complete == tasks.Length)
{
Debug.WriteLine("all tasks are completed!");
foreach (var r in records)
{
Debug.WriteLine("task {0} delay time: {1}", r.task_id, (r.start_time - r.call_time).TotalMilliseconds.ToString("0,0"));
}
}
else if (queueTask.Count > 0)
{
var bw = (BackgroundWorker)sender;
var nextTask = queueTask.Dequeue();
bw.RunWorkerAsync(nextTask);
nextTask.call_time = DateTime.Now;
}
}
这是运行后的日志结果:
all tasks are completed!
task 1 delay time: 22
task 2 delay time: 24
task 3 delay time: 24
task 4 delay time: 23
task 5 delay time: 1,005
task 6 delay time: 2,002
task 7 delay time: 3,003
task 8 delay time: 4,003
task 9 delay time: 5,004
task 10 delay time: 6,005
答案 0 :(得分:3)
管理用于ThreadPool
的线程池线程(以及其他需求)的BackgroundWorker
类不会保持无限数量的工作线程准备好运行。
您可以使用ThreadPool.SetMinThreads()
方法配置实际空闲线程数(*)。正如您在案例中所看到的,当您最初启动程序时,有四个空闲线程可以立即接受工作。默认的空闲线程数取决于与操作系统版本和配置相关的各种内容。
一旦线程池有更多排队的工作项,而不是为它们提供服务的线程,ThreadPool
类不会立即创建新线程。它会等待很短的一段时间(正如你可以从测试中看到的那样,一秒钟),假设它是可能的其中一个任务可能很快完成,它将能够重用那个线程而不是去创建另一个线程的所有麻烦(这会产生自己的开销,甚至会减慢已经运行的线程的工作量。)
通常,您应该避免覆盖线程池的默认值,因为它们通常在您的操作系统版本,硬件等情况下正确设置。例如,运行比CPU更多的线程将无济于事。你有机器上的CPU核心。让ThreadPool
类决定何时以及如何运行工作线程通常是最好的方法。
(*)以上有点过分简化了。在较新版本的.NET中,在任何给定时间,最小线程数可能实际存在,也可能不存在。如果工作项在少于最小数量时排队,ThreadPool
将根据需要立即创建新线程,最小值。除此之外,它还转向更精细的创建和调度逻辑。