2012-06-14 6 views
0

Я сделал программу, которая загружает кучу компьютерной информации. В событии 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 потоков для моих панелей это? или это немой?

EDIT

это я что я сделал для секундомера.

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(); 
    } 

Будет ли это быть точным?

EDIT 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

также есть способ времени это? –

+0

Вы говорите о батарее. Это для мобильного устройства (Win Phone, Android и т. Д.) Или для ПК/ноутбука? У ПК есть [BackgroundWorker] (http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx), что было бы здорово для этого. – jp2code

+0

@ jp2code PC Tablet. –

ответ

2

Так что мой окончательный подход это следующим образом. Я чувствовал, что моя основная форма делает больше, чем нужно. Придерживаясь принципа единой ответственности, я решил, что MainForm должен отвечать только за одно, показывать и отображать все 12 панелей (теперь до 11, я превратил один в пункт меню). Так перемещено все многопоточность из mainform и в program.cs. Я обнаружил, что это было даже немного сложнее. То, что я нашел, было простым решением, которое позволяет мне даже не беспокоиться о многопоточности вообще. Это было событие Idle. Вот что я решил сделать.

 [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 панелей и показывается на моем рабочем столе), я затем убью прослушиватель событий бездействия и расскажу всем моим панелям и классам, чтобы начать работать один за раз, обновляя мой прогресс как я иду. Он отлично работает. Время загрузки по-прежнему такое же, как и раньше, но через несколько секунд появляется видимость окна. Возможно, не лучшее использование ресурсов, но я считаю, что решение прост и прямолинейно.

7

Вы задали очень широкий вопрос, но я дам общий совет. Если вам нужна более конкретная информация, вам следует рассмотреть вопрос об удалении этого вопроса и публикации более конкретных отдельных вопросов.

  1. В первую очередь, вы должны очень строго использовать что-то вроде System.Threading.Task класса для ваших многопоточных операций. Существует тонна информации в Интернете о том, как начать с нее, и как вы можете использовать Task s для управления асинхронными операциями. Короче говоря, если вы разворачиваете свою собственную нить (как вы делаете выше), вы почти , конечно, должен использовать что-то еще, чтобы сделать это за вас.

  2. Добавление многопоточности к вашему коду не будет в строгом смысле слова сделать его «более быстрым»; они всегда будут принимать одинаковое количество процессорного времени.То, что он может и будет делать, это две вещи: освободить поток пользовательского интерфейса, чтобы реагировать и позволить вам разделить это «общее время процессора» на несколько ядер или процессоров, если они будут доступны системе. Итак, если у вас есть операция X, для завершения которой требуется 10 секунд, то просто переключение операции X в другой поток не сделает ее завершенной быстрее, чем 10 секунд.

  3. Нет, что вы делаете выше, это не сейф. Я предполагаю, что где-то вы отключили проверку ошибок связи между потоками в вашем приложении? В противном случае этот код должен выдать исключение, если предположить, что это приложение WinForms или WPF. Это одна из причин использования Task s, так как вы можете легко отделить часть вашего процесса, которая на самом деле занимает много времени (или не связана с пользовательским интерфейсом), а затем добавить продолжение задачи, которая использует результаты, и заполняет элементы пользовательского интерфейса в пределах правильно синхронизированный контекст.

+0

благодарим вас за эту информацию. Я буду использовать его сейчас и посмотреть, что он делает. –

+0

На самом деле, я не отключил связь в своей программе. Это просто стандартное приложение winforms. Я все равно займусь задачей. Мое одно изменение, но сокращение времени загрузки с 7 секунд до 3,5 секунд (в соответствии с моим тестом, который я опубликовал). Но я держу пари с вашим советом, я могу получить его еще ниже. Еще раз спасибо за ваш совет. Когда я поднимусь и работаю, и это поможет мне пометить ваш ответ, как было сказано. –

+0

Задачи для версии 4, а мои устройства имеют только .net 3.5. Так что мне придется попробовать что-то еще связанное –

1

У меня был вопрос, несколько связанного с этим для развития мобильного приложений несколько месяцев назад (см How to write a Trigger?), и Марк «человек» 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. 
    } 
    } 
} 

Я знаю, это выглядит долго, но она работает очень хорошо!

Это не что-то, что я действительно испытал, конечно, потому что данных нет. Если вы начнете работать с ним, и у вас возникнут какие-либо проблемы, сообщите мне, и я с удовольствием помогу вам в этом.

~ Юп