由于您希望在插槽可用时在 ThreadPool 中执行 InitializeCRMService,并且您只执行一次,因此解决方案取决于您要如何处理 InitializeCRMService 的返回值。
如果你只想忽略它,到目前为止我有两个选择。
选项1
public void GetConnection(string thread)
{
//I found that ops is not being used
//ParallelOptions ops = new ParallelOptions();
if(thread.Equals("one"))
{
Parallel.For(0, 1, i =>
{
//You don't really need to have a variable
/*dynamic serviceObject =*/ InitializeCRMService();
});
}
else if (thread.Equals("multi"))
{
ThreadPool.QueueUserWorkItem
(
new WaitCallback
(
(_) =>
{
//You don't really need to have a variable
/*dynamic serviceObject =*/ InitializeCRMService();
}
)
);
}
}
另一方面,如果您需要将它传递到某个地方以存储它以供以后重用,您可以这样做:
public void GetConnection(string thread)
{
//I found that ops is not being used
//ParallelOptions ops = new ParallelOptions();
if(thread.Equals("one"))
{
Parallel.For(0, 1, i =>
{
//It seems to me a good idea to take the same path here too
//dynamic serviceObject = InitializeCRMService();
Store(InitializeCRMService());
});
}
else if (thread.Equals("multi"))
{
ThreadPool.QueueUserWorkItem
(
new WaitCallback
(
(_) =>
{
Store(InitializeCRMService());
}
)
);
}
}
Store 会是这样的:
private void Store(dynamic serviceObject)
{
//store serviceObject somewhere you can use it later.
//Depending on your situation you may want to
// set a flag or use a ManualResetEvent to notify
// that serviceObject is ready to be used.
//Any pre proccess can be done here too.
//Take care of thread affinity,
// since this may come from the ThreadPool
// and the consuming thread may be another one,
// you may need some synchronization.
}
现在,如果您需要允许您的类的客户端访问 serviceObject,您可以采取以下方法:
//Note: I marked it as partial because there may be other code not showed here
// in particular I will not write the method GetConnection again. That said...
// you can have it all in a single block in a single file without using partial.
public partial class YourClass
{
private dynamic _serviceObject;
private void Store(dynamic serviceObject)
{
_serviceObject = serviceObject;
}
public dynamic ServiceObject
{
get
{
return _serviceObject;
}
}
}
但这并不能解决所有情况。特别是如果你想让线程等待 serviceObject 准备好:
public partial class YourClass
{
private ManualResetEvent _serviceObjectWaitHandle = new ManualResetEvent(false);
private dynamic _serviceObject;
private void Store(dynamic serviceObject)
{
_serviceObject = serviceObject;
//If you need to do some work as soon as _serviceObject is ready...
// then it can be done here, this may still be the thread pool thread.
//If you need to call something like the UI...
// you will need to use BeginInvoke or a similar solution.
_serviceObjectWaitHandle.Set();
}
public void WaitForServiceObject()
{
//You may also expose other overloads, just for convenience.
//This will wait until Store is executed
//When _serviceObjectWaitHandle.Set() is called
// this will let other threads pass.
_serviceObjectWaitHandle.WaitOne();
}
public dynamic ServiceObject
{
get
{
return _serviceObject;
}
}
}
不过,我还没有涵盖所有场景。例如......如果多次调用 GetConnection 会发生什么?我们需要决定是否允许这样做,如果允许,我们如何处理旧的 serviceObject?(我们需要调用一些东西来解除它吗?)。如果我们允许多个线程同时调用 GetConnection,这可能会出现问题。所以默认情况下我会说我们不这样做,但我们也不想阻塞其他线程......
解决方案?如下:
//This is another part of the same class
//This one includes GetConnection
public partial class YourClass
{
//1 if GetConnection has been called, 0 otherwise
private int _initializingServiceObject;
public void GetConnection(string thread)
{
if (Interlocked.CompareExchange(ref _initializingServiceObject, 1, 0) == 0)
{
//Go on, it is the first time GetConnection is called
//I found that ops is not being used
//ParallelOptions ops = new ParallelOptions();
if(thread.Equals("one"))
{
Parallel.For(0, 1, i =>
{
//It seems to me a good idea to take the same path here too
//dynamic serviceObject = InitializeCRMService();
Store(InitializeCRMService());
});
}
else if (thread.Equals("multi"))
{
ThreadPool.QueueUserWorkItem
(
new WaitCallback
(
(_) =>
{
Store(InitializeCRMService());
}
)
);
}
}
}
}
最后,如果我们允许多个线程使用 _serviceObject,而 _serviceObject 不是线程安全的,我们就会遇到麻烦。使用监视器或使用读写锁是解决该问题的两种选择。
你记得这个吗?
public dynamic ServiceObject
{
get
{
return _serviceObject;
}
}
好的,您希望调用者在 _serviceObject 处于阻止其他线程进入的上下文中时访问它(请参阅 System.Threading.Monitor),并确保它停止使用它,然后离开我之前提到的这个上下文。
现在考虑调用者线程仍然可以在某个地方存储 _serviceObject 的副本,然后离开同步,然后对 _serviceObject 做一些事情,这可能会在另一个线程使用它时发生。
当涉及到线程时,我习惯于考虑每一个角落情况。但是,如果您可以控制调用线程,则只需使用上面显示的属性就可以很好地做到这一点。如果你不...让我们谈谈它,我警告你,它可能是广泛的。
选项 2
这是一种完全不同的行为,Damien_The_Unbeliever在您的问题中提出的赞扬让我认为您可能打算返回 serviceObject。在这种情况下,它不是在线程之间共享的,一次有多个 serviceObject 是可以的。任何需要的同步都留给调用者。
好的,这可能是您一直在寻找的:
public void GetConnection(string thread, Action<dynamic> callback)
{
if (ReferenceEquals(callback, null))
{
throw new ArgumentNullException("callback");
}
//I found that ops is not being used
//ParallelOptions ops = new ParallelOptions();
if(thread.Equals("one"))
{
Parallel.For(0, 1, i =>
{
callback(InitializeCRMService());
});
}
else if (thread.Equals("multi"))
{
ThreadPool.QueueUserWorkItem
(
new WaitCallback
(
(_) =>
{
callback(InitializeCRMService());
}
)
);
}
}
回调应该如何看待?好吧,只要它不在线程之间共享就可以了。为什么?因为每个调用GetConnection 的线程都会传递它自己的回调Action,并且会收到不同的serviceObject,所以一个线程对其所做的事情不会影响另一个线程对其所做的事情(因为它不是同一个serviceObject)。
除非你想让一个线程调用它然后与其他线程共享它,在这种情况下,这是调用者的问题,它将在另一个地方解决。
最后一件事,您可以使用枚举来表示您当前在字符串线程中传递的选项。事实上,由于只有两个选项您可以考虑使用 bool,除非它们将来可能会出现更多情况。