1

ISP (ダウンロード クォータ) から使用状況をポーリングするアプリを開発中です。「new Thread(ThreaProc)」を介してこれをスレッド化しようとしましたが、うまくいきませんでした。現在、同じことを行う IAsyncResult ベースのアプローチを試みています...修正方法がわかりません。助けてください。

知っておくべきこと:

// Global
public delegate void AsyncPollData(ref POLLDATA pData);

// Class scope:
private POLLDATA pData;

private void UpdateUsage()  
{  
  AsyncPollData PollDataProc = new AsyncPollData(frmMain.PollUsage);  
  IAsyncResult result = PollDataProc.BeginInvoke(ref pData,  
    new AsyncCallback(UpdateDone), PollDataProc);  
}

public void UpdateDone(IAsyncResult ar)
{
  AsyncPollData PollDataProc = (AsyncPollData)ar.AsyncState;
  PollDataProc.EndInvoke(ref pData, ar);
  // The Exception occurs here:
  lblStatus.Text = pData.LastError;
}

public static void PollUsage(ref POLLDATA PData)
{
  PData.LastError = "Some string";
  return;
}
4

5 に答える 5

3

新しいクラスを自分で作成し、次のような拡張機能を作成できます。

public static class ThreadSafeHelpers {
        public static void SetText(this Label varLabel, string newText) {
            if (varLabel.InvokeRequired) {
                varLabel.BeginInvoke(new MethodInvoker(() => SetText(varLabel, newText)));
            } else {
                varLabel.Text = newText;
            }
        }
}

そして、次のようにコードのどこでもこれを使用します。

lblStatus.SetText(pData.LastError);

同じクラスCheckBoxで、のような他のもののために複数の同様の拡張機能を作成できます。RadioButtonsそうすれば、覚えやすく、拡張メソッドを使用することができます。

もちろん、次のような通常のメソッドを作成することもできます(thisラベルの横にないことに注意してください)。

public static class ThreadSafeHelpers {
        public static void SetText(Label varLabel, string newText) {
            if (varLabel.InvokeRequired) {
                varLabel.BeginInvoke(new MethodInvoker(() => SetText(varLabel, newText)));
            } else {
                varLabel.Text = newText;
            }
        }
}

次のようなコードを使用します。

ThreadSafeHelpers.SetText(varLabel, newText);
于 2011-01-09T19:40:51.290 に答える
3
lblStatus.Invoke(delegate() { lblStatus.Text = pData.LastError; });

スレッド間で値を更新することは安全ではないため、コンパイラが警告します。渡されたコードを使用Invoke()すると、GUI スレッドで呼び出されるため、GUI スレッドで GUI 値を更新しているため、安全です。

于 2011-01-09T16:47:10.213 に答える
1

あなたのコントロールはスレッドA [Windowsメッセージを描画して処理するもの]で作成されたため、スレッドB [モニター]はそれにアクセスできません[競合状態の万歳]、これを見てください:

C#で別のスレッドからGUIを更新するには?

乾杯

于 2011-01-09T16:47:26.817 に答える
0

[追加メモ: .NET 3.0 を使用しています]

「C# で別のスレッドから GUI を更新する方法は?」に記載されている方法を使用しても、その関数の他の部分 (UpdateDone) は失敗します (クラスの特定の部分が現在のスレッドの外部でアクセスされているため、スレッドとは関係のない部分でさえ失敗します)。

// This now works
DelegationClass.SetPropertyThreadSafe(lblStatus, () => lblStatus.Text, pData.LastError);
// Later on down the function, both objects are a member of the same class, the variable icon was never touched by another thread.
this.Icon = icon;
// An unhandled exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll
// Additional information: Cross-thread operation not valid: Control 'frmMain' accessed from a thread other than the thread it was created on.

間違っている場合は訂正してください。ただし、EndInvoke が呼び出されているため、スレッドは終了しているはずです。したがって、「競合状態」は存在しないはずです。すべてのデータは再びプライマリ スレッドによって所有され、データを自由に使用できるはずです...?

この構造体には、クラス全体で使用する複数の項目があります。これを行うより良い方法はありますか?どのようにしますか?

基本的な制約:
* 単一の関数がデータ ポーリングを行います。これは、UI の操作をブロックしないようにするためのものです。
* したがって、その関数は非同期でなければなりません
* メイン フォーム クラス (frmMain) は、問題の関数の完了を通知 (Async) し、必要に応じてその GUI を更新する必要があります。
* もちろん、主に、関数によって取得されたデータは、frmMain のメンバーが簡単にアクセスできる必要があります。

(なんてこった、スレッド化は C++ でとても簡単だった)

于 2011-01-09T17:31:32.837 に答える