スロットが利用可能なときに ThreadPool で InitializeCRMService を実行する必要があり、これを 1 回だけ実行しているため、解決策は InitializeCRMService の戻り値をどうしたいかによって異なります。
無視したいだけなら、今のところ 2 つの選択肢があります。
オプション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 がスレッド セーフでない場合、問題が発生する可能性があります。モニターを使用するか、読み書きロックを使用することは、それを解決するための2つの選択肢です。
これを覚えていますか?
public dynamic ServiceObject
{
get
{
return _serviceObject;
}
}
わかりました。他のスレッドが入るのを防ぐコンテキスト (System.Threading.Monitor を参照) にあるときに、呼び出し元が _serviceObject にアクセスし、その使用を停止したことを確認してから、前に述べたこのコンテキストを離れます。
ここで、呼び出し元のスレッドが _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());
}
)
);
}
}
コールバックはどのように見えるべきですか? まあ、スレッド間で共有されない限りはOKです。なんで?GetConnection を呼び出す各スレッドは、独自のコールバック アクションを渡し、異なる serviceObject を受け取るため、あるスレッドがそのスレッドに対して行うことが、他のスレッドがそのスレッドに対して行うことに影響するリスクはありません (同じ serviceObject ではないため)。
1 つのスレッドでこれを呼び出して、それを他のスレッドと共有したい場合を除きます。その場合、これは呼び出し元の問題であり、別の場所で別の瞬間に解決されます。
最後に、列挙型を使用して、文字列スレッドで現在渡しているオプションを表すことができます。実際、bool の使用を検討できるオプションは 2 つしかないため、将来さらに多くのケースが発生する可能性がある場合を除きます。