内部的代码WaitCallback
将在线程池中执行,并且可能在GetConnection
返回后执行(这就是执行异步操作的重点)。因此,由于它是另一个线程(具有另一个调用堆栈)并且它可能会在GetConnection
返回后执行,因此您不能GetConnection
从WaitCallback
. 如果你真的想这样做,那么你将不得不GetConnection
等待直到WaitCallback
完成执行。ManualResetEvent 可以解决问题:
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(如果可用,使用响应式扩展)
笔记:
- 拥有公共属性意味着您将需要处理调用者中的同步。
- 有一个回调,意味着调用该方法的一种奇怪的方式,并且没有明确的等待方式。
- 使用事件有调用者保持订阅事件处理程序的风险。
- 由于您使用的是线程池,因此返回任务似乎有点过头了。
- 使用没有响应式扩展的 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