メールクライアントにおけるマルチスレッドサポート

ImapClient や Pop3Client などのメールクライアントは、マルチスレッド環境での使用を可能にします。メールクライアントはサーバーとの1つまたは複数の接続を保持できます。クライアント内部で接続の集合を管理するために接続プールが使用されます。同時に作成・使用できる接続数は、プロパティ CredentialsByHostClient.MaxConnectionsPerServer によって制限されます。このプロパティは 1 以上の値に設定でき、デフォルトは 10 です。コマンドキューが実装されており、接続がマルチスレッド操作をサポートします。コマンドは Noop、Authenticate など、プロトコルで定義された最も基本的な操作を実装しています。ユーザーは利用可能な接続数を超える多数のコマンドの実行を開始できますが、実際に実行されるのはクライアントが操作用の接続を確保できたときだけです。

マルチスレッド環境でメールクライアントを使用する方法

メールクライアントは以下のような動作をします:

1. MaxConnectionsPerServer = 1 の場合、クライアントは 1 つの接続を作成し、認証と認可を行います。この接続はクライアントが破棄されるまで稼働状態を維持します。異なるスレッドからのすべての操作は、メイン接続に配置された単一のコマンドキューに送られます。

2. MaxConnectionsPerServer が 1 を超える場合、クライアントは必要な数の接続を作成し、各接続で認証と認可を行います。1 つの接続はメイン接続として予約され、この接続はクライアントが破棄されるまで稼働状態を維持します。他の接続は需要に応じて作成・破棄されます。このような接続の最大数はプロパティ MaxConnectionsPerServer で定義されます。例えば MaxConnectionsPerServer = 2 の場合、1 つがメイン接続として予約され、もう 1 つは他スレッドで実行される操作用の追加接続として使用されます。同様に MaxConnectionsPerServer = 3 の場合、最初の接続がメイン接続として予約され、残りの 2 つが他スレッドの操作用追加接続として使用されます。新しいスレッドから接続要求が来て、すべての接続が使用中の場合、クライアントは使用中の接続数が減少するまで待機します。これは接続の適切な破棄が重要である理由を明確にする非常に重要なポイントです。

ユーザーはさまざまな方法で異なるスレッドで操作を実行できます。これらは2つのタイプに分けられます:

1. ユーザーはクライアントで定義された非同期(Begin/End)メソッドを使用します。この場合、メールクライアントは必要に応じて新しいスレッドを起動します。クライアントにはタスクキューが実装されており(接続のコマンドキューとは混同しないでください)、接続が利用可能なときにタスクを実行できます。使用中の接続数が上限値未満になると、クライアントは新しい接続を作成し、現在のタスク用にスレッドを作成してタスクを実行します。非同期操作の使用例は以下のとおりです:

2. ユーザーは Thread、ThreadPool、Task などのオブジェクトや、目的に合わせたその他のオブジェクトを使用してスレッドを作成できます。また、サードパーティのコードで作成されたスレッドを使用することも可能です。この際、クライアントは2つの動作モデルを持ちます。

a. ユーザーがスレッド内の操作のために追加の接続を作成していない場合、そのスレッドのすべての操作はメイン接続のコマンドキューに送られます。新しい接続を作成せずに追加スレッドで操作を行う例として、すべてのトランザクションはメイン接続を介して行われます:

b. ユーザーが追加スレッド用に新しい接続を作成するメソッドを実行すると、このスレッドは新しい接続を許可するために接続クォータが変更されるまでブロックされます。その後、新しい接続が作成されます。この接続はそのスレッド内のすべての操作のデフォルト接続として設定されます。スレッド内のすべての操作が完了したら、接続は破棄しなければなりません。新しい接続を作成するには CredentialsByHostClient.CreateConnection メソッドを使用します。このメソッドは IDisposable インターフェースを実装したオブジェクトを返します。接続を解放するには Dispose メソッドを呼び出す必要があります。接続の作成と破棄は、メール操作が実行されるスレッド内で行う必要があります。メールクライアントが作成されたスレッドで新しい接続を作成しようとするとエラーが発生します。なぜならその時点のスレッドは新しい接続の作成に使用できないためです。また、MaxConnectionsPerServer = 1 の場合は新しい接続の作成はできません。以下は追加スレッドで新しい接続を作成するコード例です:

推奨事項

ユーザーがすべてのコマンドをメイン接続に送信すると、異なるスレッドからのコマンドが混在する状況が発生する可能性があることに注意が必要です。ユーザーは、どのコマンドが実行順序に依存しているかを理解し、これらのコマンドの同期策を講じる必要があります。また、異なるセッション(IMAP/POP3)でコマンドを実行する可能性も考慮すべきです。FetchMessage、AppendMessage、Send などの最も時間のかかる操作は、新しいスレッドと新しい接続で実行するのが適切でしょう。一方、Delete のような高速な操作はメイン接続で実行する方が適しています。なお、新しい接続の初期化はかなり時間がかかることに留意してください。