我制作了一个加载大量计算机信息的程序。在Form_Load事件中,我初始化了3(该数字将增长)信息面板。有一堆单位信息似乎使程序加载相当慢。我试图通过从WMI切换到使用Native调用来加快速度,这帮助了很多人。很快,我也会发布网络信息。我曾经加载那个面板,但我暂时禁用了它,直到我解决了其他面板中的错误。因此,在学习如何使用单独的线程来更新我的电池信息时,我想我可以在单元信息面板中创建单独的线程,以便它可以加载更快。我不知道我的任何信息会导致并发问题,但我可以解决这个问题。
我想从小开始,如果我改变了这个
private void Form1_Load(object sender, EventArgs e)
{
unitInformationPanel1.PopulateUnitInformation();
batteryInformationPanel1.InitializeBatteries();
magStripeReaderPanel1.SetupPointOfSale();
}
到这个
private void Form1_Load(object sender, EventArgs e)
{
Thread infoThread = new Thread(new ThreadStart(unitInformationPanel1.PopulateUnitInformation));
infoThread.Start();
batteryInformationPanel1.InitializeBatteries();
magStripeReaderPanel1.SetupPointOfSale();
}
填充单位信息完成后,信息线程会被终止吗?或者将该线程创建移动到PopulateUnitInformation中会更好吗?这就是它的样子。
public void PopulateUnitInformation()
{
unitModelLabel.Text = Properties.Settings.Default.UnitModelString;
serialNumberLabel.Text = Properties.Settings.Default.UnitSerialString;
biosVersionLabel.Text = UnitBios.GetBiosNumber();
osLabel.Text = OS.getOSString();
cpuLabel.Text = UnitCpu.GetCpuInfo();
var hdd = HddInfo.GetHddInfo();
diskNameLabel.Text = hdd.Name;
diskCapacityLabel.Text = hdd.Capacity;
diskFirmwareLabel.Text = hdd.Firmware;
memoryLabel.Text = MemoryInformation.GetTotalMemory();
NetworkPresenceInformation.GetAdapatersPresent();
biometricLabel.Text = BiometricInformation.IsPresent ? "Present" : "Not Present";
var networkAdaptersPresense = NetworkPresenceInformation.GetAdapatersPresent();
bluetoothLabel.Text = networkAdaptersPresense[0] ? "Present" : "Not Present";
wifiLabel.Text = networkAdaptersPresense[1] ? "Present" : "Not Present";
cellularLabel.Text = networkAdaptersPresense[2] ? "Present" : "Not Present";
}
-
哇我只是用infothread运行它,它仍然需要一些时间来加载(可能是我在主线程中创建的12个面板。但是它加载了12个框架,并且单元信息面板在所有加载后填充其信息。那很酷,但是它安全吗?我的面板制作12个线程有点容易吗?还是那个哑巴?修改
这就是我为秒表所做的。
Stopwatch programTimer;
public Form1()
{
programTimer = Stopwatch.StartNew();
InitializeComponent();
SetupDebugWindow();
TerminateKeymon();
UnitModel.SetModel();
UnitSerialNumber.SetSerialNumber();
}
private void Form1_Shown(object sender, EventArgs e)
{
audioBrightnessPanel1.UpdateBrightnessTrackbar();
applicationLauncherPanel1.LoadApplications();
programTimer.Stop();
Console.WriteLine("Load Time: {0}",programTimer.ElapsedMilliseconds);
timer1.Start();
}
这会准确吗?
编辑2 6/18/2012
我接受了使用backgroundworker的建议。如果我做对了,请告诉我。
private void Form1_Load(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
void BackgroundWorker1DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
unitInformationPanel1.PopulateUnitInformation();
batteryInformationPanel1.InitializeBatteries();
magStripeReaderPanel1.SetupPointOfSale();
}
答案 0 :(得分:7)
你问过一个非常广泛的问题,但我会给出一些一般性的建议。如果您需要更具体的信息,您应该考虑删除此问题并发布更具体的个别问题。
首先,您应该非常强烈地考虑使用类似System.Threading.Task
类的内容来进行多线程操作。网上有大量关于如何开始使用它的信息,以及如何使用Task
来管理异步操作。简短的故事是,如果你正在开辟自己的线程(正如你上面所做的那样),你几乎当然应该使用别的东西为你做这件事。
从最严格的意义上说,在你的代码中添加多线程不会让它变得“更快”;它们总是占用相同的处理器总时间。它能做什么和将要做的是两件事:释放UI线程以便响应,并允许您将多个核心或处理器的“总处理器时间”拆分,如果系统可用。因此,如果您的操作X需要10秒才能完成,那么只需将操作X转移到另一个线程就不会使其完成任何超过10秒的时间。
不,你上面做的是不安全。我假设您已关闭某个地方检查应用中的跨线程通信错误?否则,该代码应该抛出异常,假设这是一个WinForms或WPF应用程序。这是使用Task
的一个原因,因为您可以轻松地分离实际需要很长时间(或与UI无关)的流程部分,然后添加使用结果的任务延续并填充正确同步的上下文中的UI元素。
答案 1 :(得分:2)
所以我最后的方法如下。我觉得我的主表格做得比应有的多。坚持单一责任原则我决定MainForm应该只负责一件事,显示和显示所有12个面板(现在下降到11,我把一个变为一个菜单项)。所以将所有多线程从mainform移到program.cs中。我发现这甚至有点困难。我找到的是一个简单的解决方案,让我甚至不用担心多线程。这是空闲事件。这是我选择做的。
[STAThread]
static void Main()
{
DateTime current = DateTime.Now;
DateTime today = new DateTime(2012,7,19);
TimeSpan span = current.Subtract(today);
if (span.Days<0)
{
MessageBox.Show("Please adjust Time then restart Aspects","Adjust Time");
Process.Start("timedate.cpl").WaitForExit();
}
else
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Idle += new EventHandler(Application_Idle);
mainForm = new MainForm();
mainForm.Closing += new CancelEventHandler(mainForm_Closing);
#if !DEBUG
TerminateKeymon();
StartSerial();
SetupDefaultValues();
EmbeddedMessageBox(0);
#endif
Application.Run(mainForm);
}
}
static void Application_Idle(object sender, EventArgs e)
{
Application.Idle -= Application_Idle;
mainForm.toolStripProgressBar1.Increment(1);
UnitInformation.SetupUnitInformation();
mainForm.toolStripProgressBar1.Increment(1);
Aspects.Unit.HddInfo.GetHddInfo();
mainForm.toolStripProgressBar1.Increment(1);
for (int i = 0; i < mainForm.Controls.Count; i++)
{
if (mainForm.Controls[i] is AbstractSuperPanel)
{
try
{
var startMe = mainForm.Controls[i] as AbstractSuperPanel;
startMe.StartWorking();
mainForm.toolStripProgressBar1.Increment(1);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + mainForm.Controls[i].ToString());
}
}
}
mainForm.toolStripProgressBar1.Value = 0;
}
总结一下,我添加了一个空闲的侦听器事件。一旦thead空闲(基本上意味着Mainform完成绘制并制作所有12个面板并在我的桌面上显示)然后我杀死空闲事件监听器并告诉我的所有面板和类一次开始工作,更新我的进度我去吧。它很棒。加载时间仍然与之前相同,但仅在几秒钟后就有窗口可见。也许不是资源的最佳利用,但我认为解决方案简单直接。
答案 2 :(得分:1)
几个月前我有一个与移动应用程序开发有关的问题(参见How to write a Trigger?),而Marc“男人”Gravell发布了一个简单的类,我修改后将数据返回给我的主线程完成时应用程序。
我投入使用的实际课程中有大量无意义的数据(对你而言),所以我将使用我用来使它们工作的技术粘贴Gravell先生代码的修订版本:
首先,我必须创建自己的EventArgs
类:
public class SuperEventArgs : EventArgs {
private object data;
public SuperEventArgs(object data) : base() {
this.data = data;
}
public object Data { get { return data; } }
}
使用它,这是我创建的一个类,用于将我的数据传递回主线程:
public delegate event DataChangedHandler(object sender, SuperEventArgs e);
public class Simple1 {
private object parameter1, parameter2;
private Control parent;
#if PocketPC
public delegate void MethodInvoker(); // include this if it is not defined
#endif
public Simple1(Control frmControl, object param1, object param2) {
parent = frmControl;
parameter1 = param1;
parameter2 = param2;
}
public event DataChangedHandler DataChanged;
public void Start() {
object myData = new object(); // whatever this is. DataTable?
try {
// long routine code goes here
} finally {
if (DataChanged != null) {
SuperEventArgs e = new SuperEventArgs(myData);
MethodInvoker methInvoker = delegate {
DataChanged(this, e);
};
try {
parent.BeginInvoke(methInvoker);
} catch (Exception err) {
Log(err); // something you'd write
}
}
}
}
}
回到实际的主要执行线程中,你会做这样的事情:
public partial class Form1 : Form {
private Simple1 simple;
public Form1() {
object query = new object(); // something you want to pass in
simple = new Simple1(this, query, DateTime.Now);
simple.DataChanged += new DataChangedHandler(simple1_DataChanged);
Thread thread = new Thread(simpleStart);
thread.Start();
}
private void simpleStart() {
if (simple != null) {
simple.Start();
}
}
private void simple1_DataChanged(object sender, SuperEventArgs e) {
MyFancyData fancy = e.Data as MyFancyData;
if (fancy != null) {
// populate your form with the data you received.
}
}
}
我知道它看起来很长,但效果确实很好!
当然,这不是我实际测试的任何内容,因为没有任何数据。如果您使用它并遇到任何问题,请告诉我,我很乐意帮助您解决这些问题。
〜的Joep