[英]Using background worker with multiple classes in 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代码]
现在,该程序到目前为止运行良好,并在屏幕上显示了我要求打印的内容。 我的问题如下。 当无法计算备份作业的大小时,程序就坐在那里等待size.cs类返回该值。 我希望能够使用后台工作程序来加载程序并更新大小数字,因为它正在计算大小并将其显示在屏幕上。 这是一个控制台程序,我不确定是否能够做到这一点,但是在将来计划将其转变为基于GUI的程序时,它也将对我有帮助。 有人可以帮我这个忙吗?我一直在尝试各种事情,但没有任何效果。 我认为我的困惑是介绍背景工作者以及如何正确实施它。 谢谢您的帮助
也许我之前作为示例使用的这段代码会对您有所帮助。 这只是使用基本线程,但是与BackgroundWorker的工作方式相似,因为它使用事件来表示完成和更新。 如果您在哪里看到“通过触发事件完成信号”的注释,则说明正在触发事件以指示任务已完成并返回一些信息。 您也可以创建其他类型的事件,例如ProgressUpdate事件,然后从CalculateSize线程重复触发它,以便逐渐更新ProgressBar。 现在,我实际上将下面的内容分为多个类,而不是将它们全部混在一起,但是您明白了。 您将拥有与在一个类中处理事件相关的内容,订阅者可能是您的Form,然后线程和处理将在您的类中进行Size计算。
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));
}
/// <summary>
/// Occurs when processing has finished or an error occurred.
/// </summary>
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,不如考虑从其继承。 这使您可以更加灵活地获取和获取数据。 考虑以下示例,在此示例中,您在构造函数中传递数据并使用属性将其取出:
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"));
}
});
看看这是否可以为您提供有关后台工作人员的线索。 如果这是在Windows窗体上下文中,则您想使用控件的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();
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.