更新 : 2007 年 11 月
タイムアウトを使用して、メソッド呼び出しの完了を呼び出し元が待機する最大時間を指定します。
タイムアウトは、次のようなメソッド呼び出しパラメータの形式をとります。
server.PerformOperation(timeout)
server.PerformOperation(timeout);
または、次のようにサーバー クラスのプロパティとしても使用できます。
server.Timeout = timeout
server.PerformOperation()
server.Timeout = timeout;
server.PerformOperation();
操作とタイムアウトの関係が明確になるため、最初の方法をお勧めします。プロパティ ベースの方法は、ビジュアルなデザイナで使用されるコンポーネントとしてサーバー クラスがデザインされている場合に適しています。
従来は、タイムアウトは整数で表現されてきました。整数のタイムアウトは、タイムアウトの単位が明確でなく、時間単位を一般に使用されるミリ秒に変換するのも困難であるため、使いにくい場合があります。
もっと良い方法としては、TimeSpan 構造体をタイムアウト型として使用する方法があります。TimeSpan によって上記の整数のタイムアウトに関する問題が解決されます。TimeSpan 型のタイムアウトを使用するコード例を次に示します。
Public Class Server
Public Sub PerformOperation(timeout As TimeSpan)
' Insert code for the method here.
Console.WriteLine("performing operation with timeout {0}", _
timeout.ToString())
End Sub
End Class
public class Server
{
public void PerformOperation(TimeSpan timeout)
{
// Insert code for the method here.
Console.WriteLine("performing operation with timeout {0}",
timeout.ToString());
}
}
タイムアウトが TimeSpan(0) に設定されているときに、操作がすぐに完了しなかった場合、このメソッドは例外をスローします。タイムアウトが TimeSpan.MaxValue の場合、タイムアウトが設定されていない場合と同様に、操作はタイムアウトせずに永遠に待機したままになります。サーバー クラスでこれらの値をサポートする必要はありませんが、サポートされていないタイムアウト値が指定された場合には、ArgumentException がスローされることが必要です。
タイムアウトが経過して、例外がスローされた場合、サーバー クラスは基になる操作をキャンセルします。
既定のタイムアウトを使用する場合は、ユーザーがタイムアウトを指定しなかったときに使用するタイムアウトを指定する静的プロパティをサーバー クラスに含める必要があります。既定のタイムアウトを指定するプロパティを実装する方法を次のコード例に示します。
Class ServerWithDefault
Private Shared defaultTimeout As New TimeSpan(1000)
Public Overloads Sub PerformOperation()
Me.PerformOperation(DefaultOperationTimeout)
End Sub
Public Overloads Sub PerformOperation(timeout As TimeSpan)
' Insert code here.
Console.WriteLine("performing operation with timeout {0}", _
timeout.ToString())
End Sub
Public Shared ReadOnly Property DefaultOperationTimeout As TimeSpan
Get
Return defaultTimeout
End Get
End Property
End Class
class ServerWithDefault
{
static TimeSpan defaultTimeout = new TimeSpan(1000);
public void PerformOperation()
{
this.PerformOperation(DefaultOperationTimeout);
}
public void PerformOperation(TimeSpan timeout)
{
// Insert code here.
Console.WriteLine("performing operation with timeout {0}",
timeout.ToString());
}
public static TimeSpan DefaultOperationTimeout
{
get
{
return defaultTimeout;
}
}
}
タイムアウトを TimeSpan の精度で解決できない型では、タイムアウト値が可能な限り近い間隔に丸められます。たとえば、1 秒単位でしか待機できない型の場合は、タイムアウトが最も近い秒に丸められます。この規則の例外は、値がゼロに切り捨てられる場合です。この場合は、タイムアウトをできるだけ小さなタイムアウト値に切り上げます。ゼロのタイムアウト値を使用すると、プロセッサが 100% 使用されるビジー ウェイト ループが発生します。これを防ぐため、タイムアウトがゼロに切り捨てられることがないようにします。
さらに、タイムアウトが経過したときには、エラー コードを返すのではなく、例外をスローすることをお勧めします。タイムアウトの経過は、操作が正常に完了できなかったことを意味します。そのため、他のランタイム エラーとは異なる方法で処理する必要があります。詳細については、「例外のデザインのガイドライン」を参照してください。
タイムアウトが設定された非同期操作の場合は、操作の結果に最初にアクセスするときに、コールバック関数を呼び出し、例外をスローする必要があります。この方法を次のコード例に示します。
Sub OnReceiveCompleted(ByVal sender As System.Object, ByVal asyncResult As ReceiveCompletedEventArgs)
Dim queue As MessageQueue = CType(sender, MessageQueue)
' The following code will throw an exception
' if BeginReceive has timed out.
Dim message As Message = queue.EndReceive(asyncResult.AsyncResult)
Console.WriteLine(("Message: " + CStr(message.Body)))
queue.BeginReceive(New TimeSpan(1, 0, 0))
End Sub
void OnReceiveCompleted(Object sender, ReceiveCompletedEventArgs asyncResult)
{
MessageQueue queue = (MessageQueue) sender;
// The following code will throw an exception
// if BeginReceive has timed out.
Message message = queue.EndReceive(asyncResult.AsyncResult);
Console.WriteLine("Message: " + (string)message.Body);
queue.BeginReceive(new TimeSpan(1,0,0));
}
Portions Copyright 2005 Microsoft Corporation.All rights reserved.
Portions Copyright Addison-Wesley Corporation.All rights reserved.
デザイン ガイドラインの詳細については、2005 年に Addison-Wesley から出版されている Krzysztof Cwalina、Brad Abrams 共著の『Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries』を参照してください。