5

Control.BeginInvoke を「ファイア アンド フォーゲット」方式以外で使用することは可能ですか? 次のリクエストを変更してコールバック メソッドをデリゲートし、各非同期呼び出しが完了したときに何かを実行できるようにします。

this.BeginInvoke(new RefreshRulesDelegate(RefreshRules), new object[] { ctrl, ctrl.DsRules, ctrl.CptyId });  

通常のデリゲートでこれを行うことができます.BeginInvokeなど

RefreshRulesDelegate del = new RefreshRulesDelegate(RefreshRules);
            del.BeginInvoke(ctrl, ctrl.DsRules, ctrl.CptyId, new AsyncCallback(RefreshCompleted), del);  

しかし、Control .BeginInvoke を呼び出しているため、「クロススレッド操作が無効です」というエラーが発生するため、これを行うことはできません。
誰か助けて?

受け取った回答のいくつかに加えて、「なぜ」を明確にします。アプリの残りの部分をロックせずに、GUI でコントロールをロード/更新する必要があります。コントロールには、データセットを取得して渡す必要がある多数のコントロール (ruleListCtls) が含まれています。すなわち

public void RefreshAll()
{
    foreach (LTRFundingRuleListControl ctrl in ruleListCtls)
    {
        this.BeginInvoke(new RefreshRulesDelegate(RefreshRules), new object[]{ctrl,ctrl.DsRules, ctrl.CptyId });   
    }
}  

デリゲート コールバック メソッドを提供し、コントロールを修正するコードをそれらが作成されたメインの GUI スレッドに戻すと、これを実行できることがわかりました (クロススレッド エラーを回避するため)。

public void RefreshAll()
{
    IntPtr handle; 
    foreach (LTRFundingRuleListControl ctrl in ruleListCtls)
    {
        handle = ctrl.Handle;
        RefreshRulesDsDelegate del = new RefreshRulesDsDelegate(RefreshRulesDs);
        del.BeginInvoke(ctrl.DsRules, ctrl.CptyId, handle, out handle, new AsyncCallback(RefreshCompleted), del);
    }        
}

private void RefreshCompleted(IAsyncResult result)
{
    CptyCatRuleDataSet dsRules;
    string cptyId;
    IntPtr handle;

    AsyncResult res = (AsyncResult) result;

    // Get the handle of the control to update, and the dataset to update it with
    RefreshRulesDsDelegate del = (RefreshRulesDsDelegate) res.AsyncDelegate;
    dsRules = del.EndInvoke(out handle,res);

    // Update the control on the thread it was created on
    this.BeginInvoke(new UpdateControlDatasetDelegate(UpdateControlDataset), new object[] {dsRules, handle});
}

public delegate CptyCatRuleDataSet RefreshRulesDsDelegate(CptyCatRuleDataSet dsRules, string cptyId, IntPtr ctrlId, out IntPtr handle);
private CptyCatRuleDataSet RefreshRulesDs(CptyCatRuleDataSet dsRules, string ruleCptyId, IntPtr ctrlId, out IntPtr handle)
{
    try
    {
        handle = ctrlId;
        int catId = ((CptyCatRuleDataSet.PSLTR_RULE_CAT_CPTY_SelRow)dsRules.PSLTR_RULE_CAT_CPTY_Sel.Rows[0]).RULE_CAT_ID;
            return ltrCptyRulesService.GetCptyRules(ruleCptyId, catId);
    }
    catch (Exception ex)
    {
        throw ex;
    }
}  

コールバックを受信したメイン スレッドに委譲する内容は次のとおりです。

private delegate void UpdateControlDatasetDelegate(CptyCatRuleDataSet dsRules, IntPtr ctrlId);
private void UpdateControlDataset(CptyCatRuleDataSet dsRules, IntPtr ctrlId)
{
    IEnumerator en = ruleListCtls.GetEnumerator();
    while (en.MoveNext())
    {
        LTRFundingRuleListControl ctrl = en.Current as LTRFundingRuleListControl;
        if (ctrl.Handle == ctrlId)
        {
            ctrl.DsRules = dsRules;
        }
    }
}  

これで問題なく動作します。ただし、これが特にエレガントだとは思わないことを除けば、主な問題は例外処理です。これは別の質問かもしれませんが、RefreshRulesDs が例外をスローすると、エラーが (明らかに) GUI スレッドにバブルアップされず、未処理の例外として発生するため、アプリがクラッシュします。これらをキャッチできるまで、この操作全体を同期的に行う必要があります。エラーをキャッチして残りのコントロールを読み込むにはどうすればよいですか? または、適切な例外処理を使用して、この非同期操作を別の方法で実現するにはどうすればよいですか?

4

3 に答える 3

4

「可能ですか」の部分について:いいえ、Control.BeginInvokeWindowsを使用しPostMessage()ています。これは答えがないことを意味します。また、RefreshRulesDelegateがバックグラウンドスレッドではなく、メインスレッドで実行されることも意味します。

したがって、delegate.BeginInvokeまたはThreadPoolを使用し、それらが完了したらControl.[Begin]Invoke()、UIを更新するために使用します。

于 2009-11-27T13:32:42.540 に答える
2

あなたはこれを行うことができます:

this.BeginInvoke(delegate
{
    RefreshRules(ctrl, ctrl.DsRules, ctrl.CptyId);
    RefreshCompleted();
});

編集:

メソッド RefreshCompleted から IAsyncResult 引数を削除し、上記のソリューションを使用することを検討します。

何らかの理由で IAsyncResult 引数を保持する必要がある場合。Control の拡張メソッドを実装できます。

public static IAsyncResult BeginInvoke(this Control control, Delegate del, object[] args, AsyncCallback callback, object state)
{
    CustomAsyncResult asyncResult = new CustomAsyncResult(callback, state);
    control.BeginInvoke(delegate
    {
        del.DynamicInvoke(args);
        asyncResult.Complete();
    }, args);

    return asyncResult;
}

public static void EndInvoke(this Control control, IAsyncResult asyncResult)
{
    asyncResult.EndInvoke();
}

CustomAsyncResult クラスを定義する必要があります。これを行う方法に関するドキュメントはこちらから入手できます。

于 2009-11-27T13:05:57.870 に答える
0

では、ワーカー スレッドで「余分な処理」を実行したいですか? (それ以外の場合は、メソッドで実行するだけですRefreshRules)。おそらく単に使用しますThreadPool.QueueUserItem

ThreadPool.QueueUserWorkItem(delegate { /* your extra stuff */ });

メソッドの終わり(または後)にRefreshRules

詳細についてはBeginInvoke、匿名メソッドでも呼び出す方が簡単/整頓されている場合があります。

this.BeginInvoke((MethodInvoker) delegate {
    RefreshRules(ctrl, ctrl.DsRules, ctrl.CptyId);
    ThreadPool.QueueUserWorkItem(delegate { /* your extra stuff */ });
});

これにより、デリゲート型の作成が回避され、呼び出しで型チェックが提供されますRefreshRules-ただし、キャプチャすることに注意してくださいctrl-ループにいる場合は、コピーが必要になります:

var tmp = ctrl;
this.BeginInvoke((MethodInvoker) delegate {
    RefreshRules(tmp, tmp.DsRules, tmp.CptyId);
    ThreadPool.QueueUserWorkItem(delegate { /* your extra stuff */ });
});
于 2009-11-27T13:03:54.673 に答える