の内部のコードWaitCallback
はスレッド プールで実行され、おそらくGetConnection
が戻った後に実行されます (これが非同期操作を実行するポイントです)。したがって、これは別のスレッド (別のコール スタックを持つ) であり、 が戻った後に実行される可能性があるため、 の内部から戻るGetConnection
ことはできません。本当にそれをしたい場合は、 の実行が完了するまで待機する必要があります。ManualResetEvent はトリックを実行できます。GetConnection
WaitCallback
GetConnection
WaitCallback
public IOrganizationService GetConnection(bool multi)
{
var waitHandle = new ManualResetEvent(false);
dynamic result = null;
if(!multi)
{
Parallel.For(0, 1, i =>
{
result = InitializeCRMService();
waitHandle.Set();
});
}
else
{
ThreadPool.QueueUserWorkItem
(
new WaitCallback
(
(_) =>
{
result = InitializeCRMService();
waitHandle.Set();
}
)
);
}
//We wait until the job is done...
waitHandle.WaitOne();
return result as IOrganizationService; //Or use an adecuate casting
}
しかし、これを行うことは、そもそも非同期操作を行うという点に反します。呼び出し元のスレッドは、別のスレッドでジョブが完了するまで待機する必要があるため、何もせずにそこに座って... それでは、同期して実行してみませんか? 一言で言えば:無意味。
問題は、値を直接返すことが同期 API であることです。非同期操作が必要な場合は、非同期 API が必要になります。非同期 API を使用する場合は、呼び出し元の動作方法を変更する必要があります。
ソリューションは次のとおりです。
- reuslt にアクセスするためのパブリック プロパティを持つ(オプション 1)
- コールバックを持つ(オプション 2)
- イベントへのリソース
- タスクを返す (または、利用可能な場合は非同期キーワークを使用する)
- IObservable を返す (利用可能な場合は Reactive Extensions を使用)
ノート:
- public プロパティを持つということは、呼び出し元で同期を処理する必要があることを意味します。
- コールバックがあるということは、メソッドを呼び出す奇妙な方法であり、明示的に待機する方法がないことを意味します。
- イベントを使用すると、呼び出し元がイベント ハンドラーにサブスクライブされたままになるリスクがあります。
- スレッドプールを使用しているため、タスクを返すのはやり過ぎのようです。
- Reactive Extension を使用せずに IObservable を使用すると、エラーが発生しやすくなり、他の方法に比べてはるかに多くの作業が必要になります。
個人的には、コールバック オプションを使用します。
public void GetConnection(bool multi, Action<IOrganizationService> callback)
{
if (ReferenceEquals(callback, null))
{
throw new ArgumentNullException("callback");
}
if(!multi)
{
Parallel.For(0, 1, i =>
{
callback(InitializeCRMService() as IOrganizationService);
//Or instead of using "as", use an adecuate casting
});
}
else
{
ThreadPool.QueueUserWorkItem
(
new WaitCallback
(
(_) =>
{
callback(InitializeCRMService() as IOrganizationService);
//Or instead of using "as", use an adecuate casting
}
)
);
}
}
次に、呼び出し元は次のようにします。
GetConnection
(
false,
(seriveObject) =>
{
/* do something with seriveObject here */
}
);
//Remember, even after GetConnection completed seriveObject may not be ready
// That's because it is asyncrhonous: you want to say "hey Bob do this for me"
// and you can go do something else
// after a while Bob comes back an says:
// "that thing you asked me to do? well here is the result".
// We call that a callback, and the point is that you didn't have to wait for Bob
// you just kept doing your stuff...
//So... when is seriveObject ready? I don't know.
//But when seriveObject is ready the callback will run and then you can use it