1

バックグラウンド:

完了するまでに数秒以上かかる複数の操作を含むクラスがあります。それまでの間、UIを更新したいと思います。したがって、通常は BackgroundWorker を使用します。しかし、何らかの理由で、BackGroundWorker が常に思いどおりに動作するとは限りません (例: イベントで WebBrowser を使用し、ReportProgress イベントを呼び出そうとすると、BackgroundWorker がクラッシュしたように見えます)。

そのため、Ui をメインスレッドから分離することで、これらすべてを回避しています。

この疑似コードはそれをよりよく説明しています:

public Ui ui;

main
{
    Thread threadUi = new Thread(initiateUi);      
    //Initiate and start Thread

    //Everything I will do from here on will not have any consequences 
    //on my ui.
    //
    //Object Ui can still be publicly accessed, making it possible to 
    //update the user interface.
}

クラス Bar のインスタンスがある場合、次のように UI からアクセスできるようにします。

public Bar bar1;
public Bar bar2;

main
{
    //
    //other stuff here
    //

    Thread threadBar1 = //New Thread where I call the Bar initializer function
                        //and pass bar1 as parameter.
    Thread threadBar2 = //idem dito, except with bar2 as parameter

    //
    //other stuff here
    //
}

この設計では、次の関数を使用してユーザー インターフェイスから bar1 と bar2 を呼び出すことができます。

Program.bar1.someFunction();

問題:

ここで、FooHandler というクラスがあるとします。このクラスには、特定の FooDepository 内の Foo のすべてのインスタンスを検索する関数と、Foo オブジェクトを操作するその他の関数があります。私の場合、複数のインスタンスを持つ必要がないため、これは静的クラスです。

しかし、FooHandler から関数を呼び出す場合、関数は UI スレッドで実行されます。これは呼び出しスレッドであるためです (よくわかりませんが、この件に関するドキュメントは見つかりませんでした)。ですから、私が始めた問題に直面しようとしている可能性は十分にあります。

質問:

呼び出しスレッドの処理能力を使用せずに静的クラスの関数にアクセスすることは可能ですか?

4

2 に答える 2

3

まず第一に、メソッド スコープ (定義されている場所) は、プログラム フローとは何の関係もありません。メソッドが定義されている場所 (FooHandler、BarProvider、または ThreadX) は、呼び出される場所には影響しません。実際には、メソッドは常に呼び出し元のスレッドで呼び出されます。

モデル、ビュー、ビューモデルについて言及しておらず、タイトルに「c#」と書かれているため、WinFormsについて話していると思います。

WinForms UI コントロールでは、それらを作成するために使用されたスレッド (通常はメイン スレッド) から呼び出す (更新する) 必要があります。すべての UI コントロールは、それを行うための ISynchronizeInvoke インターフェイスを実装しています。したがって、通常の代わりに:

progress.Position = 7;

あなたが呼び出す必要がありますInvoke

progress.Invoke(new Action(() => progress.Position = 7), null)

定型コードがたくさんあるので、自分用に小さな拡張関数を書きました。

public static class ControlExtensions
{
    public static void Synchronize(this Control control, Action action)
    {
        if (control == null || !control.InvokeRequired)
        {
            action();
        }
        else
        {
            control.Invoke(action, null);
        }
    }
}

したがって、次のことができます。

progress.Synchronize(() => progress.Position = 7);

(タイピングが少なくなり、読みやすくなります)

技術的には、ISynchronizeTarget の Invoke は実際には特定のアクションを呼び出しません。引数としてデリゲートを使用して、メッセージ (古き良き WM_xxxx) をメッセージ キューに入れるだけです (ただし、これは呼び出し元のスレッドで行います)。次に、ターゲット (コントロールの) スレッドが (独自のスレッドで) メッセージを処理している場合、この WM_xxxx メッセージを取得し、(呼び出し元のスレッドで - ただし、今回は UI スレッドである) デリゲートを呼び出して戻ります。

FooHandler を呼び出すために新しい Thread が必要で、Tasks を使用するのを待ちたくない場合 (おそらく最も簡単な方法です):

Task.Factory.StartNew(() => FooHandler.SearchOrWhatever(...));

待機しません (UI スレッドをブロックしません)。

このすべてが言われているにもかかわらず、それが完了したと思い込まないでください。マルチスレッドは難しい。そして、サポートするすべての構造はタイピングの手間を省きますが、まだ難しい部分があります: デッドロック、競合状態、飢餓などです。

于 2013-06-28T08:14:08.250 に答える
2

別のスレッドを使用してこの関数を呼び出すことで可能です。.NET 4 を使用している場合は、この問題を簡単に解決できる Task オブジェクトを見てください。たとえば、関数が文字列を返す場合、Task<string>関数を呼び出す必要があります。次に、ロジックに応じて、完了するまでブロックするか、同様のことを行います。.NET 4.5 を使用している場合は、async/await を使用するとさらに簡単になります。

于 2013-06-28T07:39:28.957 に答える