在C#中使用具有多个类的后台工作程序

我正在学习使用C#进行编程,并且已经完成了大部分基础知识。 我无法使用后台工作程序并将其与多个类一起使用。 这是我正在编写的备份程序,我有以下类。

lacie.cs —>用于搜索备份设备main.cs —>主条目size.cs —>确定备份的大小xml.cs —>读取目录的xml配置文件备份。

到目前为止,我将展示main.cs中的内容。

[main.cs代码]

using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace QXBackup { class main { static void Main(string[] args) { lacie BackupDrive = new lacie(); BackupDrive.findLacie(); xml xmlFile = new xml(); xmlFile.ProcessXML(); size BackupSize = new size(); BackupSize.GetSize(xmlFile.Path); int SizeofBackup = (int)(((BackupSize.BackupSize) / 1024f) / 1024f) / 1024; Console.WriteLine("Drive Letter: " + BackupDrive.Drive); Console.WriteLine("Volume Name: " + BackupDrive.VolumeLabel); Console.WriteLine("Free Space: " + Convert.ToString(BackupDrive.AvailableSize) + "G"); Console.WriteLine("Size of Lacie: " + Convert.ToString(BackupDrive.TotalSize) + "G"); Console.WriteLine("Backup Size: " + Convert.ToString(SizeofBackup + "G")); Console.WriteLine("Backing up " + BackupSize.FileCount + " files found in " + BackupSize.FolderCount + " folders."); Console.ReadKey(true); } } } 

[结束main.cs代码]

现在该程序工作正常,并显示我要求它在屏幕上打印。 我的问题如下。 当它计算备份作业的大小时,程序就坐在那里等待teh size.cs类返回值。 我希望能够使用后台工作程序加载程序并更新大小编号,因为它正在计算大小并在屏幕上显示。 这是一个控制台程序,我不确定我是否能够做到这一点,但它也将帮助我,因为我计划在将来将其转换为基于GUI的程序。 有人可以帮助我,我一直在尝试各种各样的事情,没有任何作用。 我认为我的困惑是介绍后台工作者以及如何正确实现它。 谢谢您的帮助

也许这个我之前用过的代码可以帮助你。 这只是使用基本线程,但它与BackgroundWorker的工作方式类似,它使用事件来指示完成和更新。 如果您查看注释“通过触发事件来完成信号”,则会触发一个事件以指示任务已完成并返回一些信息。 您也可以创建其他类型的事件,如ProgressUpdate事件,并从CalculateSize线程重复触发,以便您可以逐步更新ProgressBar。 现在我实际上将以下分成多个类而不是全部混合在一起,但你明白了。 您将拥有与在一个类中处理事件相关的内容,订阅者可能是您的表单,然后线程和处理将在您的类中进行大小计算。

 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Threading; namespace ThreadWithDataReturnExample { public partial class Form1 : Form { private Thread thread1 = null; public Form1() { InitializeComponent(); thread1 = new Thread(new ThreadStart(this.threadEntryPoint)); Thread1Completed += new AsyncCompletedEventHandler(thread1_Thread1Completed); } private void startButton_Click(object sender, EventArgs e) { thread1.Start(); //Alternatively, you could pass some object //in such as Start(someObject); //With apprioriate locking, or protocol where //no other threads access the object until //an event signals when the thread is complete, //any other class with a reference to the object //would be able to access that data. //But instead, I'm going to use AsyncCompletedEventArgs //in an event that signals completion } void thread1_Thread1Completed(object sender, AsyncCompletedEventArgs e) { if (this.InvokeRequired) {//marshal the call if we are not on the GUI thread BeginInvoke(new AsyncCompletedEventHandler(thread1_Thread1Completed), new object[] { sender, e }); } else { //display error if error occurred //if no error occurred, process data if (e.Error == null) {//then success MessageBox.Show("Worker thread completed successfully"); DataYouWantToReturn someData = e.UserState as DataYouWantToReturn; MessageBox.Show("Your data my lord: " + someData.someProperty); } else//error { MessageBox.Show("The following error occurred:" + Environment.NewLine + e.Error.ToString()); } } } #region I would actually move all of this into it's own class private void threadEntryPoint() { //do a bunch of stuff //when you are done: //initialize object with data that you want to return DataYouWantToReturn dataYouWantToReturn = new DataYouWantToReturn(); dataYouWantToReturn.someProperty = "more data"; //signal completion by firing an event OnThread1Completed(new AsyncCompletedEventArgs(null, false, dataYouWantToReturn)); } ///  /// Occurs when processing has finished or an error occurred. ///  public event AsyncCompletedEventHandler Thread1Completed; protected virtual void OnThread1Completed(AsyncCompletedEventArgs e) { //copy locally AsyncCompletedEventHandler handler = Thread1Completed; if (handler != null) { handler(this, e); } } #endregion } } 

正如您的问题请求,这将向您展示如何使用后台工作程序组件。 这应该足够了,但是,当你进入更复杂的线程使用时,最好像aaronls提供的那样写一些东西。

您的Size类应该包含backgroundWorker线程,而BackupSize.GetSize("PathHere")应该进行异步调用以启动后台工作线程。

在你的工作方法中,你可以通过调用backgroundWorker.ReportProgress(i);来报告工作进度backgroundWorker.ReportProgress(i);

在您的ReportProgress委托内,您可以创建一个可以由您的主gui连接的事件通知

 class main { static void Main(string[] args) { size BackupSize = new size(); BackupSize.GetSize("path"); BackupSize.ProgressEvent += new ProgressEventHandler(BackupSize_ProgressEvent); // BackupSize.BackupSize will not be accurate until the thread is finished. // You may want to take that into consideration int SizeofBackup = (int)(((BackupSize.BackupSize) / 1024f) / 1024f) / 1024; Console.ReadLine(); } static void BackupSize_ProgressEvent(object source, int progress) { Console.WriteLine(String.Format("Progress: {0}", progress)); } } // This is the delegate that acts as the event handler for your progress events public delegate void ProgressEventHandler(object source, int progress); public class size { private readonly BackgroundWorker backgroundWorker; public event ProgressEventHandler ProgressEvent; public size() { backgroundWorker = new BackgroundWorker { WorkerReportsProgress = true }; backgroundWorker.DoWork += backgroundWorker_DoWork; backgroundWorker.ProgressChanged += backgroundWorker_ProgressChanged; } public void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) { // do something with progress ProgressEvent.Invoke(sender, e.ProgressPercentage); } void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) { String Path = e.Argument as String; // Do something with Path; // Simulate work for (int i = 1; i <= 100; i++) { // Get the size of the Here // Report the Progress backgroundWorker.ReportProgress(i); Thread.Sleep(10); } } public void GetSize(String path) { backgroundWorker.RunWorkerAsync(path); } } 

而不是按原样使用BackgroundWorker,请考虑inheritance它。 这使您可以更灵活地获取数据的输入和输出。 考虑这个示例,您在构造函数中传入数据并使用属性将其取出:

 class BackgroundDoerOfSomething : BackgroundWorker { string _SomeData; public string SomeResult { get; private set; } public BackgroundDoerOfSomething(string someData) { _SomeData = someData; } protected override void OnDoWork(DoWorkEventArgs e) { base.OnDoWork(e); // do some processing, and then assign the result SomeResult = "some other data"; } } 

你会像这样使用它:

 class DoSomethingInBackground { BackgroundDoerOfSomething _doer; void DoSomething() { _doer = new BackgroundDoerOfSomething("abc"); _doer.RunWorkerCompleted += _doer_RunWorkerCompleted; _doer.RunWorkerAsync(); } void _doer_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { var myResult = _doer.SomeResult; // then do something with the result } } 

在不考虑需要更新GUI(需要执行control.Invoke())的情况下,这是使用ThreadPool在后台运行某些东西的一种非常简单的方法。 关于线程池的好处是你不必担心创建,处理和跟踪线程。

 static void Main(string[] args) { lacie BackupDrive = new lacie(); BackupDrive.findLacie(); xml xmlFile = new xml(); xmlFile.ProcessXML(); size BackupSize = new size(); System.Threading.ThreadPool.QueueUserWorkItem(s => { BackupSize.GetSize(xmlFile.Path); }); int SizeofBackup = (int)(((BackupSize.BackupSize) / 1024f) / 1024f) / 1024; Console.WriteLine("Drive Letter: " + BackupDrive.Drive); Console.WriteLine("Volume Name: " + BackupDrive.VolumeLabel); Console.WriteLine("Free Space: " + Convert.ToString(BackupDrive.AvailableSize) + "G"); Console.WriteLine("Size of Lacie: " + Convert.ToString(BackupDrive.TotalSize) + "G"); Console.WriteLine("Backup Size: " + Convert.ToString(SizeofBackup + "G")); Console.WriteLine("Backing up " + BackupSize.FileCount + " files found in " + BackupSize.FolderCount + " folders."); Console.ReadKey(true); } 

您可以向线程池中添加其他内容,这样就可以像这样写入控制台:

 System.Threading.ThreadPool.QueueUserWorkItem(s => { while (true) // might want a real condition like while(!backupNotDone) { int SizeofBackup = (int) (((BackupSize.BackupSize)/1024f)/1024f)/1024; Console.WriteLine("Backup Size: " + Convert.ToString(SizeofBackup + "G")); } }); 

看看这是否可以为您提供背景工作者的线索。 如果这是在窗体窗体上下文中,您希望使用控件的BeginInvoke来通过其拥有的线程更新UI。 例如txtMessage.Invoke(UpdateMyMsg)

 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace QXBackup { class main { static void Main(string[] args) { var bgWorker = new BackgroundWorker(); bgWorker.WorkerReportsProgress = true; bgWorker.DoWork += (sender, e) => { lacie BackupDrive = new lacie(); BackupDrive.findLacie(); xml xmlFile = new xml(); xmlFile.ProcessXML(); size BackupSize = new size(); BackupSize.GetSize(xmlFile.Path); int SizeofBackup = (int)(((BackupSize.BackupSize) / 1024f) / 1024f) / 1024; Console.WriteLine("Drive Letter: " + BackupDrive.Drive); Console.WriteLine("Volume Name: " + BackupDrive.VolumeLabel); Console.WriteLine("Free Space: " + Convert.ToString(BackupDrive.AvailableSize) + "G"); Console.WriteLine("Size of Lacie: " + Convert.ToString(BackupDrive.TotalSize) + "G"); Console.WriteLine("Backup Size: " + Convert.ToString(SizeofBackup + "G")); Console.WriteLine("Backing up " + BackupSize.FileCount + " files found in " + BackupSize.FolderCount + " folders."); Console.ReadKey(true); }; bgWorker.RunWorkerCompleted += (sender, e) => Console.WriteLine("completed..."); bgWorker.ProgressChanged += (sender, e) => Console.WriteLine("progressing..."); bgWorker.RunWorkerAsync(); } } }