0

マルチスレッドプログラミングモデルについて明確にする必要があります。なぜなら、私がそれに対処しなければならないことはめったにないからです (ほとんどの場合、私は Web プログラミングを行っています)。

前例

私はC#で作成したプログラムを持っています。これは基本的にデータモニターであり、データベース全体で特定の時間ごとにプーリングを実行します.1分としましょう。

このアプリケーションが比較的単純である理由 モノリシック構造を使用することに決め、すべての GUI をさまざまなクラスで作成し、接続とデータ取得を処理するオブジェクトを作成しました。

を使用したプーリング時間の要求に対処するためにSystem.Timers.Timer、これは基本的に発火時限イベントを使用したスレッド ラッピングであり、ここでデータ プーリングを実行しました。

新しいデータが見つかったら、タイマーによってトリガーされた関数 (定義により他のスレッド内) から、GUI (現在はメイン スレッドで実行中) に属する X クラスのメソッドを起動する必要があります。

タイマーによってトリガーされたメソッドにメソッドを直接呼び出そうとしたのは初めてで、クロススレッド エラーが発生しました。メソッドが X クラスに存在し、タイマー スレッドで構築されていないためです。

次に、他のアプローチを使用して再試行しました。データを読み取り、GUI オブジェクトをそのイベントにサブスクライブするクラスで、それぞれのデリゲートを使用してイベントを作成しました。

問題

今、私がイベントを発生させると、.netは送信者オブジェクトインスタンスがnullであるという例外を生成します.理由は正確にはわかりませんが、オブジェクト自体がメインスレッドのGUIオブジェクトで作成されたためだと思われます。 (イベントを生成している) 他のスレッドからオブジェクトを送信することを決定した場合、インスタンスはそこに作成されていないため存在しません。

これは、タイマーが切れたときに呼び出される関数です。

    private void PoolearMensajes(object Sender, ElapsedEventArgs e)
    {
        try
        {
            pRdr.getPendingMessages(); <--- This function creates the exception
            if (pRdr.MensajesPendientes > 0)
            {
                this.oSysIcon.Text = pRdr.MensajesPendientes.ToString() + " tickets pendientes";
                this.oSysIcon.BalloonTipIcon = ToolTipIcon.Info;
                this.oSysIcon.BalloonTipText = pRdr.MensajesPendientes.ToString() + " tickets pendientes";
                this.oSysIcon.BalloonTipTitle = "Información";
                this.oSysIcon.ShowBalloonTip(3000);
                System.Media.SystemSounds.Exclamation.Play();
            }
        }
        catch (Exception ex)
        {
            this.oSysIcon.BalloonTipTitle = "Error";
            this.oSysIcon.Text = "Ha ocurrido un error.";
            this.oSysIcon.BalloonTipIcon = ToolTipIcon.Error;
            this.oSysIcon.BalloonTipText = ex.Message;
            this.oSysIcon.ShowBalloonTip(3000);
            System.Media.SystemSounds.Exclamation.Play();
        }
    }

以下は、メイン スレッドで作成されたデータ取得オブジェクトに存在するメソッドです。

    public delegate void TicketsFoundHandler(object sender, TicketEventArgs ta);
    public event TicketsFoundHandler TicketsFound;

    public void getPendingMessages()
    {
        string sSQL;
        sSQL = " an SQL Query that counts rows;";
        oCmd = new SqlCommand(sSQL, oConn);
        oConn.Open();
        this.i_pendmsg = (int)oCmd.ExecuteScalar();
        if (this.i_pendmsg > 0)
        {
            TicketEventArgs ta = new TicketEventArgs("", this.i_pendmsg);
            TicketsFound(this, ta); <---- ERROR!
        }
        oConn.Close();
    }

エラーメッセージ: Object reference not set to an instance of an object.

アイデア、改善点、または私が考慮していないものはありますか?

4

1 に答える 1

2

タイマーによってトリガーされたメソッドにメソッドを直接呼び出そうとしたのは初めてで、クロススレッド エラーが発生しました。メソッドが X クラスに存在し、タイマー スレッドで構築されていないためです。

あなたはほとんどそこにいました。BeginInvoke()GUI スレッドで操作を呼び出すために呼び出すだけです。

BackgroundWorkerまた、 a を使用する方がさらに簡単であることに気付くかもしれません。

編集:

質問の特定のエラーは、TicketsFoundイベントに何もサブスクライブしていないためです。そのために使用TicketsFound += ...します。また、イベントを発生させる前に null をチェックします。

if (TicketsFound != null) TicketsFound(...)
于 2012-10-31T16:45:50.037 に答える