0

大量のコンピューター情報をロードするプログラムを作成しました。Form_Load イベントでは、3 つの情報パネル (その数は増えます) を初期化します。ユニット情報がたくさんあるものは、プログラムのロードがかなり遅くなるようです。私は、WMI からネイティブ呼び出しを使用するように切り替えることで、一連の処理を高速化しようとしましたが、これは非常に役に立ちました。近々、ネットワーク情報も掲載する予定です。以前はそのパネルをロードしていましたが、他のパネルのバグを解決するまで少しの間無効にしました。別のスレッドを使用してバッテリー情報を更新する方法を学びながら、ユニット情報パネルに別のスレッドを作成して、より速くロードできるようにできるかもしれないと考えました。私の情報のいずれかが同時発生の問題を引き起こすかどうかはわかりませんが、私はそれに取り組むことができます.

小さく始めたいのでこれを変えたらどうですか

    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 2012 年 6 月 18 日

さて、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();
    }
4

3 に答える 3

7

あなたは非常に幅広い質問をしましたが、私はいくつかの一般的なアドバイスをするつもりです。より具体的な情報が必要な場合は、この質問を削除して、より具体的な個別の質問を投稿することを検討してください。

  1. 何よりもまず、System.Threading.Taskマルチスレッド操作にクラスのようなものを使用することを非常に強く検討する必要があります。Taskそれを開始する方法と、非同期操作を管理するためにsを使用する方法については、オンラインで大量の情報があります。簡単に言うと、(上記のように)自分のスレッドをスピンアップしている場合は、ほぼ確実に他の何かを使用してそれを実行する必要があります。

  2. コードにマルチスレッドを追加しても、厳密な意味では、コードが「高速」になるわけではありません。それらは常に同じ合計プロセッサ時間を要します。それができることと実行することは2つのことです。UIスレッドを解放して応答性を高め、システムで利用可能な場合は、その「合計プロセッサ時間」を複数のコアまたはプロセッサに分割できるようにします。したがって、完了までに10秒かかる操作Xがある場合、操作Xを別のスレッドにシフトするだけでは、10秒より速く完了することはありません。

  3. いいえ、上記で行っていることは安全ではありません。アプリのクロススレッド通信エラーのチェックをどこかでオフにしたと思いますか?それ以外の場合、これがWinFormsまたはWPFアプリケーションであると想定して、そのコードは例外をスローする必要があります。これがsを使用する理由の1つです。これはTask、実際に時間がかかる(またはUIに関連しない)プロセスの部分を簡単に分離し、結果を使用してUI要素を適切に入力するタスク継続を追加できるためです。同期されたコンテキスト。

于 2012-06-14T16:11:11.253 に答える
2

したがって、私の最終的なアプローチは次のとおりです。メイン フォームが必要以上に機能しているように感じました。単一の責任の原則に固執して、MainForm は 12 のパネルすべてを表示および表示するという 1 つのことだけを担当する必要があると判断しました (現在は 11 に減り、1 つをメニュー項目に変えました)。そのため、すべてのマルチスレッドをメインフォームから 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;
    }

要約すると、アイドル リスナー イベントを追加します。スレッドがアイドル状態になったら (基本的に、Mainform が描画を終了し、12 個のパネルすべてを作成してデスクトップに表示していることを意味します)、アイドル状態のイベント リスナーを強制終了し、すべてのパネルとクラスに一度に 1 つずつ作業を開始し、進行状況を更新するように指示します。私が行くようにバー。それはうまくいきます。読み込み時間は以前と同じですが、わずか数秒後にウィンドウが表示されます。リソースの最適な使用法ではないかもしれませんが、解決策はシンプルで簡単だと思います。

于 2012-09-22T15:52:03.387 に答える
1

数か月前に、モバイル アプリの開発に関してこれに関連する質問がありました (トリガーの作成方法を参照してください)。Marc "the man" Gravell は、いつでもメイン アプリケーションにデータを返すように変更した単純なクラスを投稿しました。スレッドは完了しました。

私が使用した実際のクラスには無意味なデータが大量に含まれているため (あなたにとって)、私がそれらを機能させるために使用した手法を使用して、Mr. 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

于 2012-06-14T19:35:08.147 に答える