메일 클라이언트의 다중 스레드 지원

ImapClient와 Pop3Client와 같은 메일 클라이언트는 사용자가 멀티스레드 환경에서 이를 사용할 수 있도록 허용합니다. 메일 클라이언트를 사용하면 서버와 하나 이상의 연결을 가질 수 있습니다. 클라이언트 내부에서 연결 집합을 관리하기 위해 연결 풀을 사용합니다. 동시에 생성하고 사용할 수 있는 연결 수는 CredentialsByHostClient.MaxConnectionsPerServer 속성에 의해 제한됩니다. 이 속성은 1 이상의 값으로 설정할 수 있으며 기본값은 10입니다. 멀티스레드 작업을 지원하기 위해 연결에 명령 대기열이 구현되었습니다. 명령은 Noop, Authenticate 등과 같은 프로토콜에 정의된 가장 간단한 작업을 구현합니다. 사용자는 사용 가능한 연결 수보다 많은 명령을 실행하도록 시작할 수 있지만, 명령은 클라이언트가 작업을 위한 연결을 생성할 수 있을 때만 실행됩니다.

멀티스레드 환경에서 메일 클라이언트를 사용하는 방법

이메일 클라이언트는 다음과 같은 동작을 합니다:

1. MaxConnectionsPerServer = 1인 경우, 클라이언트는 1개의 연결을 생성하고 인증 및 인가를 수행합니다. 이 연결은 클라이언트가 해제될 때까지 작업 상태를 유지합니다. 서로 다른 스레드의 모든 작업은 기본 연결에 배치된 하나의 명령 큐로 전달됩니다.

2. MaxConnectionsPerServer > 1인 경우, 클라이언트는 필요량만큼 연결을 생성하고 각 연결에 대해 인증 및 인가를 수행합니다. 하나의 연결은 기본 연결로 예약됩니다. 이 연결은 클라이언트가 해제될 때까지 작업 상태를 유지합니다. 다른 연결은 필요에 따라 생성·해제됩니다. 이러한 연결의 최대 수는 MaxConnectionsPerServer 속성으로 정의되며, 예를 들어 MaxConnectionsPerServer = 2이면 하나는 기본 연결로 예약되고, 두 번째 연결은 다른 스레드에서 실행되는 작업을 위한 추가 연결로 사용됩니다. MaxConnectionsPerServer = 3이면 첫 번째 연결이 기본 연결로 예약되고, 두 개의 추가 연결이 다른 스레드 작업에 사용됩니다. 새로운 스레드에서 연결 요청이 들어오고 모든 연결이 사용 중이면, 사용 중인 연결 수가 감소할 때까지 클라이언트는 대기합니다. 이는 연결을 올바르게 해제하는 것이 왜 중요한지를 명확히 하는 매우 중요한 상황입니다.

예제

사용자는 여러 방법으로 다른 스레드에서 작업을 실행할 수 있습니다. 이는 두 가지 유형으로 구분됩니다:

1. 사용자는 클라이언트에 정의된 비동기(Begin/End) 메서드를 사용합니다. 이 경우 메일 클라이언트는 필요에 따라 새로운 스레드를 시작합니다. 클라이언트에는 작업 큐가 구현되어 있는데(연결의 명령 큐와 혼동하지 마세요), 연결이 사용 가능하면 작업이 실행됩니다. 사용 중인 연결 수가 제한값보다 적어지면 클라이언트는 새 연결을 만들고 현재 작업을 위한 스레드를 생성하여 해당 작업을 실행합니다. 비동기 작업 사용 예시:

2. 사용자는 Thread, ThreadPool, Task와 같은 객체 또는 이 목적을 위한 다른 객체를 사용하여 스레드를 만들 수 있습니다. 또한 서드파티 코드에서 생성된 스레드를 사용할 수도 있습니다. 이때 클라이언트는 두 가지 동작 모델을 가집니다

a. 사용자가 스레드 내 작업을 위해 추가 연결을 만들지 않은 경우, 해당 스레드의 모든 작업은 명령 큐를 통해 기본 연결로 전송됩니다. 새로운 연결을 만들지 않고 추가 스레드에서 수행되는 작업 예시로, 모든 트랜잭션이 기본 연결을 통해 이루어집니다:

b. 사용자가 추가 스레드용 새로운 연결을 생성하는 메서드를 실행하면, 새 연결을 허용하도록 연결 할당량 값이 변경될 때까지 해당 스레드가 차단됩니다. 그 후 새 연결이 생성됩니다. 이 연결은 해당 스레드의 모든 작업에 대한 기본 연결로 설정됩니다. 스레드의 모든 작업이 완료된 후 연결을 해제해야 합니다. 새로운 연결을 만들려면 CredentialsByHostClient.CreateConnection 메서드를 사용하십시오. 이 메서드는 IDisposable 인터페이스를 구현하는 객체를 반환합니다. 연결을 해제하려면 Dispose 메서드를 호출해야 합니다. 연결 생성 및 해제는 메일 작업이 실행되는 스레드 내에서 수행되어야 합니다. 메일 클라이언트가 생성된 스레드에서 새로운 연결을 만들려고 하면 오류가 발생하는데, 해당 스레드는 현재 새로운 연결을 생성하는 데 사용할 수 없기 때문입니다. 또한 MaxConnectionsPerServer = 1인 경우 새로운 연결을 만들 수 없습니다. 추가 스레드에서 새로운 연결을 생성하는 코드 예시:

권장 사항

사용자가 모든 명령을 기본 연결에 보낼 경우, 서로 다른 스레드의 명령이 섞이는 상황이 발생할 수 있다는 점에 유의해야 합니다. 사용자는 어떤 명령이 순서에 의존하는지 이해하고, 이러한 명령들의 동기화를 위해 조치를 취해야 합니다. 또한 다른 세션(IMAP/POP3)에서 명령을 실행하는 가능성도 고려해야 합니다. FetchMessage, AppendMessage, Send와 같은 가장 시간이 많이 소요되는 작업은 새 스레드와 새 연결을 사용하여 수행하는 것이 바람직합니다. 하지만 Delete와 같은 빠른 작업은 기본 연결에서 수행하는 것이 적합합니다. 새 연결 초기화 자체가 충분히 시간이 소요되는 작업임을 기억하십시오.