多线程以加快加载时间

时间:2012-06-14 16:02:21

标签: c# multithreading load-time

我制作了一个加载大量计算机信息的程序。在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();
    }

3 个答案:

答案 0 :(得分:7)

你问过一个非常广泛的问题,但我会给出一些一般性的建议。如果您需要更具体的信息,您应该考虑删除此问题并发布更具体的个别问题。

  1. 首先,您应该非常强烈地考虑使用类似System.Threading.Task类的内容来进行多线程操作。网上有大量关于如何开始使用它的信息,以及如何使用Task来管理异步操作。简短的故事是,如果你正在开辟自己的线程(正如你上面所做的那样),你几乎当然应该使用别的东西为你做这件事。

  2. 从最严格的意义上说,在你的代码中添加多线程不会让它变得“更快”;它们总是占用相同的处理器总时间。它能做什么和将要做的是两件事:释放UI线程以便响应,并允许您将多个核心或处理器的“总处理器时间”拆分,如果系统可用。因此,如果您的操作X需要10秒才能完成,那么只需将操作X转移到另一个线程就不会使其完成任何超过10秒的时间。

  3. 不,你上面做的是安全。我假设您已关闭某个地方检查应用中的跨线程通信错误?否则,该代码应该抛出异常,假设这是一个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