0

提供されているDLLからルーチンを呼び出す必要があります。このDLLは、実行されているPCの速度にもよりますが、実行に2〜10分かかります。

インターフェイスが応答し続けるように、DLL呼び出しをBackgroundWorkerスレッドに入れました。

private object Load(string feature) {
  object result = null;
  using (var w = new BackgroundWorker()) {
    w.WorkerReportsProgress = true;
    w.WorkerSupportsCancellation = true;
    w.DoWork += delegate(object sender, DoWorkEventArgs e) {
      e.Result = DAL.get(feature);
    };
    w.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e) {
      progressBar1.Visible = false;
      if (e.Error != null) {
        MessageBox.Show(this, e.Error.Message, "Load Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
      } else {
        result = e.Result;
      }
    };
    w.RunWorkerAsync();
    if (w.IsBusy) {
      progressBar1.Style = ProgressBarStyle.Marquee;
      progressBar1.Visible = true;
    }
  }
  return result;
}

これは機能しますが、結果を待っている他の呼び出しとインラインでこのメソッドを呼び出すことはできません。これは、すぐにnull値を返すためです。

そのため、ManualResetEventインスタンスを使用して、メソッドが実際に値を取得するまで待機してから、次の値を返すようにしました。

private object Load(string feature) {
  object result = null;
  using (var w = new BackgroundWorker()) {
    var mre = new ManualResetEvent(false);
    w.WorkerReportsProgress = true;
    w.WorkerSupportsCancellation = true;
    w.DoWork += delegate(object sender, DoWorkEventArgs e) {
      e.Result = DAL.get(feature);
    };
    w.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e) {
      progressBar1.Visible = false;
      if (e.Error != null) {
        MessageBox.Show(this, e.Error.Message, "Model Load Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
      } else {
        result = e.Result;
      }
      mre.Set();
    };
    w.RunWorkerAsync();
    if (w.IsBusy) {
      progressBar1.Style = ProgressBarStyle.Marquee;
      progressBar1.Visible = true;
      progressBar1.Value = 0;
      const string statusRun = @"\|/-";
      const string statusMsg = "Loading Data...";
      int loops = 0;
      do {
        int index = loops++ % 4;
        tsStatus.Text = statusMsg + statusRun[index].ToString(); // '\', '|', '/', '-'
      } while (!mre.WaitOne(200));
    }
  }
  return result;
}

ただし、これを行うと、CPU時間がすべてManualResetEventのWaitOneメソッドに費やされ、Set()トリガーが呼び出されないようです。

誰かが以前にこの動作に遭遇し、それに対する良い回避策を見つけたことがありますか?

過去に回避策を作成しましたが、最初のスレッドがコードWaitOneを処理できるように、メソッドを実行するための2番目のスレッドを作成する必要がありました。DoWork

4

1 に答える 1

2

これは機能しますが、結果を待機している他の呼び出しとインラインでこのメソッドを呼び出すことはできません。これは、すぐに null 値が返されるためです。

asyncそれがandawaitキーワードが考案された理由です。スレッドをブロックしない簡単な解決策はありません。

BeginOperation使用している DLL は、非同期プログラミング パターン ( / EndOperation、またはタスクベースの非同期プログラミングなど)を使用して実装されたメソッドを明らかに提供していないため、別のワーカー スレッドで立ち往生しています。

あなたができることは次のとおりです。

BackgroundWorkerorを通常どおり起動し、Threadすぐに戻ります。長い DLL プロセスの戻り値を必要とする操作を続行しないでください。BackgroundWorkerorが終了したらThread、進行状況を報告し、ProgressChangedイベント ハンドラーで、長い DLL プロセスの結果を取得して操作を続行できます。または、イベントを使用することもできますRunWorkerCompleted(実際には、より良い選択かもしれません)。

それまでの間、時間のかかる DLL プロセスを再び開始する可能性がある、またはプロセスの実行中に無効な操作を発行する可能性のあるすべてのコントロールを無効にする必要がある場合があります。Henk Holterman が書いたように、そのように処分しないでくださいBackgroundWorker

于 2012-10-03T14:39:30.097 に答える