Я согласен с другими ответами, рекомендующими использовать пул потоков .NET вместо того, чтобы запускать новые потоки самостоятельно. Если вы можете позволить себе роскошь использовать .NET 4 или новее, я бы пошел еще дальше и рекомендовал бы вам использовать Task Parallel Library (и даже если вы в настоящее время не используете .NET 4, создание ориентированной на задачи абстракция поверх пула потоков .NET очень проста).
Одним из самых больших преимуществ использования задач IMHO является то, что они являются основой новой встроенной языковой функции C # 5 await
. Поэтому, если вы используете задачи для фоновой обработки сегодня, вы уже готовите свою программу на будущее: -).
Чтобы показать, как вы можете использовать задачи для решения вашей непосредственной проблемы, я написал простую программу WinForms, которая иллюстрирует технику выполнения фоновой обработки и обновления индикатора выполнения, чтобы отслеживать, сколько задач уже выполнено:
// file "Program.cs"
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ProgressDialog
{
static class Program
{
[STAThread] static void Main()
{
// build UI
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var rootForm = new Form { Text = @"Test Form", Width = 300, Height = 100 };
var btn = new Button { Text = @"Start", Parent = rootForm, Dock = DockStyle.Top };
var progress = new ProgressBar { Minimum = 0, Parent = rootForm, Dock = DockStyle.Top, Style = ProgressBarStyle.Continuous };
new Label { Text = @"Progress:", Parent = rootForm, Dock = DockStyle.Top, AutoSize = true };
// define parameters
const int sourcesCount = 20; // how many sources do we want to process
var completedCount = 0;
var randomGenerator = new Random();
var timer = new Stopwatch();
// callback that will be invoked on UI thread each time a task finishes
Action<int> onTaskCompleted = source =>
{
++completedCount; // we're modifying "completedCount" closure on purpose
progress.Value = completedCount;
System.Diagnostics.Debugger.Log(0, null, string.Concat(@"UI notified that task for source ", source, @" finished; overall ", completedCount, @" tasks finished", Environment.NewLine));
if (completedCount == sourcesCount)
{
timer.Stop();
btn.Enabled = true;
btn.Text = string.Concat(@"Finished (took ", timer.ElapsedMilliseconds, @" milliseconds). Start again");
}
};
// task itself (the hard part :) )
Action<int> task = source =>
{
System.Diagnostics.Debugger.Log(0, null, string.Concat(@" > Starting task for source ", source, Environment.NewLine));
Thread.Sleep(randomGenerator.Next(100, 200)); // simulate some workload (taking between 100 and 200 milliseconds)
System.Diagnostics.Debugger.Log(0, null, string.Concat(@" < Finished task for source ", source, Environment.NewLine));
rootForm.BeginInvoke(new Action(() => onTaskCompleted(source)));
};
// start button handler (kick-starts the background tasks)
btn.Click += (src, args) =>
{
btn.Enabled = false;
btn.Text = @"Running...";
progress.Maximum = sourcesCount;
progress.Value = 0;
timer.Restart();
completedCount = 0;
var sources = Enumerable.Range(1, sourcesCount); // simulate getting data for each task
var tasks = sources
.Select(s => Task.Factory.StartNew(() => task(s))) // at this point we only have an enumerable that is able to start all the tasks, nothing is running yet
.ToArray(); // now the tasks are started
if (tasks.Length != sourcesCount) { throw new InvalidOperationException(); } // assert that we created one task for each source
};
// show the form now, let the user interact with it
Application.Run(rootForm);
}
}
}
Вы можете скомпилировать программу, создав новый проект (console или winforms) в Visual Studio и скопировав код в Program.cs
или используя csc.exe
в командной строке.
Когда задачи выполняются, индикатор выполнения отслеживает количество выполненных задач:

Когда все задачи завершены, кнопка «Пуск» отображает общее время:

Обратите внимание, что время, затрачиваемое каждой задачей, является случайным (от 100 до 200 миллисекунд), и количество одновременно выполняемых задач будет зависеть от того, сколько процессоров / ядер у вас имеется (Task Parallel Library делает это автоматически), поэтому отображается будет варьироваться между пробегами.
Также обратите внимание, что диагностические сообщения отправляются в представление «Вывод» в Visual Studio при запуске программы в режиме отладки (или вы можете использовать SysInternals DebugView для их просмотра). Один пробный прогон на моем (2-ядерном) компьютере произвел следующее:
> Starting task for source 1
> Starting task for source 2
> Starting task for source 3
< Finished task for source 3
> Starting task for source 4
UI notified that task for source 3 finished; overall 1 tasks finished
< Finished task for source 2
> Starting task for source 5
UI notified that task for source 2 finished; overall 2 tasks finished
< Finished task for source 1
> Starting task for source 6
UI notified that task for source 1 finished; overall 3 tasks finished
< Finished task for source 4
> Starting task for source 7
UI notified that task for source 4 finished; overall 4 tasks finished
< Finished task for source 5
> Starting task for source 8
UI notified that task for source 5 finished; overall 5 tasks finished
< Finished task for source 6
> Starting task for source 9
UI notified that task for source 6 finished; overall 6 tasks finished
< Finished task for source 8
> Starting task for source 10
UI notified that task for source 8 finished; overall 7 tasks finished
< Finished task for source 7
> Starting task for source 11
UI notified that task for source 7 finished; overall 8 tasks finished
< Finished task for source 9
> Starting task for source 12
UI notified that task for source 9 finished; overall 9 tasks finished
< Finished task for source 10
< Finished task for source 11
> Starting task for source 13
UI notified that task for source 10 finished; overall 10 tasks finished
UI notified that task for source 11 finished; overall 11 tasks finished
> Starting task for source 14
< Finished task for source 14
> Starting task for source 15
UI notified that task for source 14 finished; overall 12 tasks finished
< Finished task for source 13
> Starting task for source 16
UI notified that task for source 13 finished; overall 13 tasks finished
< Finished task for source 12
> Starting task for source 17
UI notified that task for source 12 finished; overall 14 tasks finished
< Finished task for source 16
> Starting task for source 18
UI notified that task for source 16 finished; overall 15 tasks finished
< Finished task for source 15
UI notified that task for source 15 finished; overall 16 tasks finished
> Starting task for source 19
< Finished task for source 17
UI notified that task for source 17 finished; overall 17 tasks finished
< Finished task for source 18
> Starting task for source 20
UI notified that task for source 18 finished; overall 18 tasks finished
< Finished task for source 19
UI notified that task for source 19 finished; overall 19 tasks finished
< Finished task for source 20
UI notified that task for source 20 finished; overall 20 tasks finished