我同意其他答案建議使用.NET線程池,而不是自己開始新線程。如果您有使用.NET 4或更高版本的優點,我會走得更遠,並建議您使用Task Parallel Library(即使您目前不使用.NET 4,也可以在.NET線程的基礎上構建面向任務的抽象,游泳池非常簡單)。
使用任務恕我直言,最大的優點之一是,他們是即將到來的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);
}
}
}
您可以通過在Visual Studio中創建新(控制檯或winforms)項目並將代碼複製到Program.cs
或在命令行上使用csc.exe
來編譯該程序。
當任務運行時,進度條軌道已完成的任務數:
當所有任務都完成後,啓動按鈕顯示拍攝總時間:
注每個任務所花費的時間是隨機的(100到200毫秒之間),同時運行的任務數將取決於您有多少處理器/內核可用(任務P arallel Library會自動執行此操作),所以顯示的時間將在不同運行之間變化。
另請注意,診斷消息在調試模式下運行程序時(或者可以使用SysInternals DebugView查看它們)發送到Visual Studio中的「輸出」視圖。我(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
使用線程池(OF-話題,但強烈建議) – Burimi