9

Web ページで、タイムアウトをプログラムで設定することを許可していないサード パーティに電話しています。BeginInvoke を呼び出し、AsyncWaitHandle.WaitOne を使用して、指定された時間待機します。

呼び出しがタイムアウトした場合は、先に進み、開始したスレッド呼び出しを忘れます。私の質問は、タイムアウトの状況で何らかの方法で EndInvoke を呼び出す必要があるかどうかです。この MSDN ページの "CAUTION" 発言は、私がすべきかどうか疑問に思います: http://msdn.microsoft.com/en-us/library/2e08f6yc(VS.71).aspx

私がすべきだと思うなら、次の質問は、私の Web ページの処理が完了し、サード パーティが戻ってくる前にクライアントに戻る場合、コードを実行するためにコールバック メソッドがリッスンするかどうかです。リクエスト/レスポンスが完了すると、サーバーはアクティビティの検索を停止しませんか?

私が使用しているコードは次のとおりです。

public class RemotePaymentProcessor
{
    private delegate string SendProcessPaymentDelegate(string creditCardNumber);

    private string SendProcessPayment(string creditCardNumber)
    {
        string response = string.Empty;
        // call web service
        SlowResponseService.SlowResponseService srs = new WebServiceTimeout.SlowResponseService.SlowResponseService();
        response = srs.GetSlowResponse(creditCardNumber);
        return response;
    }

    public string ProcessPayment(string creditCardNumber, int timeoutMilliseconds)
    {
        string response = string.Empty;

        SendProcessPaymentDelegate sppd = new SendProcessPaymentDelegate(SendProcessPayment);
        IAsyncResult ar = sppd.BeginInvoke(creditCardNumber, null, new object());
        if (!ar.AsyncWaitHandle.WaitOne(timeoutMilliseconds, false))
        {
            // Async call did not return before timeout
            response = "TIMEOUT";
        }
        else
        {
            // Async call has returned - get response
            response = sppd.EndInvoke(ar);
        }
        return response;
    }
}
4

4 に答える 4

2

EndInvoke を呼び出さないと、一部のリソース (BeginInvoke によって割り当てられる) がリークする可能性があります。

したがって、完全に安全にするために、常に EndInvoke() を呼び出します (これはブロックされるため、必要のないバックグラウンド スレッドで実行するか、コールバックを渡すように切り替えて、待機中にスレッドを焼き切らないようにします)。

実際には、それがどのくらいの頻度で問題になるかはわかりません (多くの AsyncResults はリークしないと思うので、この場合は幸運で安全かもしれません)。

これはすべて、リクエスト/レスポンスや Web サーバーなどとはほとんど関係がありません。これは、使用しているアプリケーションに関係なく、Begin/End プログラミング モデルがどのように機能するかだけです。

于 2009-02-17T18:11:12.220 に答える
1

更新:非同期呼び出し (その Control.BeginInvoke を除く) に対して常に EndInvoke
を呼び出す必要があるようです。そうしないと、リソースがリークする危険があります。

これは、同じ行にある議論です。推奨される解決策は、デリゲートが実際に完了するまで待機するスレッドを生成し、EndInvoke を呼び出すことです。ただし、実際に Looong のタイムアウトが発生した場合は、スレッドがハングするだけだと思います。

よく文書化されていないように見えるそのエッジケース...おそらくcozタイムアウトは発生しないはずです..例外的なケース

于 2009-02-17T18:03:47.010 に答える
0

EndInvoke を確実に呼び出さないとリソースがリークする可能性があるというアドバイスを無視することはできませんでした。 .

私が見つけた解決策は、非同期コールバック関数を使用していました。呼び出しが時間内に返された場合は、そこで EndInvoke を呼び出します。そうでない場合は、ボタンのクリックを続行し、非同期コールバック関数に EndInvoke を使用して混乱を解消させます。

Web アプリに関する私自身の質問に答えるために、「タイムアウトして先に進んだ後、誰かが聞いてくれますか?」クライアントにすでに出力を返している場合でも、 call は戻り、コールバック関数を実行します。

http://www.eggheadcafe.com/tutorials/aspnet/847c94bf-4b8d-4a66-9ae5-5b61f049019f/basics-make-any-method-c.aspxで見つけたものの一部を使用しました。

...他の場所で見つけたコールバックと組み合わせることもできます。以下に、私が行ったことの小さなサンプル関数を示します。これは、Thanks for everyone's input! で見つけたものの一部を組み合わせたものです。

public class RemotePaymentProcessor
{    
    string currentResponse = string.Empty;

    private delegate string SendProcessPaymentDelegate(string creditCardNumber);    
    private string SendProcessPayment(string creditCardNumber)    
    {        
        SlowResponseService.SlowResponseService srs = new WebServiceTimeout.SlowResponseService.SlowResponseService();        
        string response = srs.GetSlowResponse(creditCardNumber);        
        return response;    
    }    

    public string ProcessPayment(string creditCardNumber, int timeoutMilliseconds)    
    {        
        string response = string.Empty;        
        SendProcessPaymentDelegate sppd = new SendProcessPaymentDelegate(SendProcessPayment);        
        IAsyncResult ar = sppd.BeginInvoke(creditCardNumber,  new AsyncCallback(TransactionReturned), sppd);        
        if (!ar.AsyncWaitHandle.WaitOne(timeoutMilliseconds, false))        
        {            
            // Async call did not return before timeout            
            response = "TIMEOUT";        
        }        
        else            
        {            
            // Async call has returned - get response            
            response = sppd.EndInvoke(ar);        
        }        

        currentResponse = response; // Set class variable
        return response;    
    }

    private void TransactionReturned(IAsyncResult ar)
    {
        string response = string.Empty;

        // Get delegate back out of Async object
        SendProcessPaymentDelegate sppd = (SendProcessPaymentDelegate)ar.AsyncState;

        // Check outer class response status to see if call has already timed out
        if(currentResponse.ToUpper().Equals("TIMEOUT"))
        {
            // EndInvoke has not yet been called so call it here, but do nothing with result
            response = sppd.EndInvoke(ar);
        }
        else
        {
            // Transaction must have returned on time and EndInvoke has already been called.  Do nothing.
        }       

    }
}
于 2009-03-11T19:51:16.150 に答える
0

.NET 非同期パターンの一般的なケースでは、操作が完了する前に EndXXX が呼び出された場合、操作が完了するまでブロックする必要があるため、BeingXXX で開始された操作を完了したくないときに EndXXX を呼び出すのは誤りです。これは、タイムアウトにはあまり役に立ちません。

特定の API は異なる場合があります (たとえば、WinForms は明示的に EndInvoke を必要としません)。

「フレームワーク設計ガイドライン」(第2版)の§9.2を参照。またはmsdn

于 2009-02-17T18:44:38.397 に答える