WCFでデッドロックまたはタイムアウトが発生
5549 ワード
WCF电脑里的Dedlock
一、サーバー側デッドロック
次のサービスの場合:
[ServiceContract(CallbackContract = typeof(INotify))]
public class DownloadService
{
[OperationContract]
public void Download()
{
//
//.....
//
var callback = OperationContext.Current.GetCallbackChannel<INotify>();
callback.DownloadComplete();
}
}
interface Inotify
{
[OperationContract]
void DownloadComplete();
}
まず、クライアントとしてコンソールプログラムを使用します.コードは次のとおりです.
class Program
{
static void Main(string[] args)
{
var context = new System.ServiceModel.InstanceContext(new CallBack());
var client = new DownloadServiceClient(context);
client.Download();
}
}
class CallBack : DownloadServiceCallback
{
public void DownloadComplete()
{
Console.WriteLine("finished");
}
}
このプログラムを実行すると、次のような異常が発生します.
未処理の異常:System.ServiceModel.FaultException`1[System.ServiceModel.ExceptionDetail]:現在のメール処理が完了するまで返信が来ないため、この操作はデッドロックになります.無秩序なメール処理を許可する場合は、ServiceBehaviorAttributeで再入力可能なまたは複数のConcurrencyModeを指定します.
この異常は、現在のサービスではコンカレントがサポートされておらず、Download関数を処理するときにチャネルがシリアルであり、メッセージを返すときはDownload関数の処理メッセージを返してから、DownloadCompleteコールバックを返してコールバックの結果を取得しなければならないことが明らかになった.しかし、Download関数自体はDownloadComplete関数の戻りを待っており、デッドロックが形成されています.
この例外は、サービスのServiceBehaviorのConcurrencyModeをReentrantまたはMultipleに変更すればよいという修正案も示しています.
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant)] public class DownloadService
実際、この問題にはもう一つの解決策があります.コールバックの完了を待つ必要がなければ、コールバック関数をOneWayに設定することができます.これによりDownload関数はコールバック関数の戻り結果を待たず,互いに待つことでデッドロックを招くことはない.
interface Inotify
{
[OperationContract(IsOneWay=true)]
void DownloadComplete();
}
二、クライアントのデッドロック
システム自身がサーバ側のデッドロックを分析できるため、サーバ側のデッドロックは発見しやすく処理しやすい.しかし、クライアントのデッドロックは容易に発見されません.
前の処理後、サーバ側のデッドロック問題は解決され、クライアントは順調に処理できます.今回はクライアントコードをWinFormバージョンのプログラムに入れ,UIスレッドで処理する.
private void button1_Click(object sender, EventArgs e)
{
var context = new System.ServiceModel.InstanceContext(new CallBack());
var client = new DownloadServiceClient(context);
client.Download();
}
上記のコードを実行すると、ボタンをクリックすると、ウィンドウが応答しないことがわかります.WCFのコールバック関数のデフォルトはUIスレッドで実行されるため、Download関数がDownloadCompleteコールバックの実行を待ってから戻るのを待つDownloadCompleteコールバックが発生します.DownloadCompleteコールバックはDownload関数がDownload関数が戻ってUIスレッドを解放するのを待っているため、デッドロックが発生します.コンソールプログラムにはUIスレッドがなく、このようなデッドロックは発生しません.
このようなデッドロックの場合、根本的な方法は、コールバックコール関数の通知スレッドを変更し、非UIスレッドで実行することである.WCFでは、クライアントコールバック関数クラスのCallbackBehaviorAttributeでこの動作を制御できます.
[CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant, UseSynchronizationContext = false)]
class CallBack : DownloadServiceCallback
ここではCallbackBehaviorAttributeのConcurrencyModeパラメータも設定しましたが、このパラメータはこの例では必要ありませんが、習慣的に持ってきました.いつ使うかは、読者の皆さんにご分析ください.
[ServiceContract(CallbackContract = typeof(INotify))]
public class DownloadService
{
[OperationContract]
public void Download()
{
//
//.....
//
var callback = OperationContext.Current.GetCallbackChannel<INotify>();
callback.DownloadComplete();
}
}
interface Inotify
{
[OperationContract]
void DownloadComplete();
}
class Program
{
static void Main(string[] args)
{
var context = new System.ServiceModel.InstanceContext(new CallBack());
var client = new DownloadServiceClient(context);
client.Download();
}
}
class CallBack : DownloadServiceCallback
{
public void DownloadComplete()
{
Console.WriteLine("finished");
}
}
interface Inotify
{
[OperationContract(IsOneWay=true)]
void DownloadComplete();
}
private void button1_Click(object sender, EventArgs e)
{
var context = new System.ServiceModel.InstanceContext(new CallBack());
var client = new DownloadServiceClient(context);
client.Download();
}
[CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant, UseSynchronizationContext = false)]
class CallBack : DownloadServiceCallback