Ondersteuning voor multithreading in e-mailclients

Mailclients zoals ImapClient en Pop3Client stellen gebruikers in staat ze te gebruiken in een multithread-omgeving. Mailclients kunnen één of meer verbindingen met een server hebben. Om een set verbindingen binnen clients te beheren, wordt een verbindingspool gebruikt. Het aantal verbindingen dat tegelijkertijd kan worden gecreëerd en gebruikt, wordt beperkt door de eigenschap CredentialsByHostClient.MaxConnectionsPerServer. Deze eigenschap kan worden ingesteld op 1 of een hogere waarde. Standaard is deze eigenschap gelijk aan 10. Er is een opdrachtwachtrij geïmplementeerd voor de verbinding om multithread-bewerkingen te ondersteunen. Opdrachten implementeren simpelste bewerkingen gedefinieerd in het protocol, zoals Noob, Authenticate enzovoort. De gebruiker kan meer opdrachten starten dan beschikbare verbindingen. Maar ze worden pas uitgevoerd wanneer de client een verbinding kan maken voor de bewerking.

Methoden voor het gebruiken van mailclients in een multithread-omgeving

E‑mailclients hebben het volgende gedrag:

1. In het geval dat MaxConnectionsPerServer = 1, maakt de client één verbinding aan, voert authenticatie en autorisatie uit. Deze verbinding blijft in werkende staat tot de client wordt verwijderd. Alle bewerkingen van verschillende threads worden naar één commando‑wachtrij gestuurd die zich in de hoofdverbinding bevindt.

2. In het geval dat MaxConnectionsPerServer > 1, maakt de client het benodigde aantal verbindingen aan, voert authenticatie en autorisatie uit voor elke verbinding. Eén verbinding wordt gereserveerd als hoofdverbinding. Deze verbinding blijft in werkende staat totdat de client wordt verwijderd. Alle andere verbindingen worden op aanvraag aangemaakt en verwijderd. Het maximale aantal dergelijke verbindingen wordt bepaald door de eigenschap MaxConnectionsPerServer, dus bijvoorbeeld als MaxConnectionsPerServer = 2, dan is één gereserveerd als hoofdverbinding en wordt de tweede verbinding als extra gebruikt voor bewerkingen die in andere threads worden uitgevoerd. Evenzo, als MaxConnectionsPerServer = 3, dan is de eerste verbinding gereserveerd als hoofdverbinding en worden de twee andere verbindingen als extra gebruikt voor bewerkingen in andere threads. Komt er een nieuw verzoek om een verbinding van een nieuwe thread en zijn alle verbindingen al in gebruik, dan wacht de client tot het aantal gebruikte verbindingen wordt verminderd. Dit is een zeer belangrijk moment dat verduidelijkt waarom het correct vrijgeven van verbindingen zo belangrijk is.

Voorbeelden

De gebruiker kan bewerkingen in verschillende threads op verschillende manieren uitvoeren. Ze kunnen worden onderverdeeld in twee typen:

1. De gebruiker gebruikt asynchrone (Begin/End) methoden die in de client zijn gedefinieerd. In dit geval start de e‑mailclient nieuwe threads wanneer nodig. In de client is een taak‑wachtrij geïmplementeerd (let op, dit is niet te verwarren met de commando‑wachtrij in de verbinding). Een taak kan worden uitgevoerd als er een verbinding beschikbaar is. Zodra het aantal gebruikte verbindingen onder de limietwaarde komt, maakt de client een nieuwe verbinding, start een thread voor de huidige taak en voert deze taak uit. Voorbeeld van het gebruik van asynchrone bewerkingen:

2. De gebruiker kan threads maken met objecten zoals Thread, ThreadPool, Task of andere daarvoor bestemde objecten. Ook kan de gebruiker threads gebruiken die in externe code zijn gecreëerd. Tijdens dit proces heeft de client twee gedragsmodellen

a. Als de gebruiker geen extra verbindingen heeft aangemaakt voor bewerkingen in de thread, worden alle bewerkingen voor deze thread naar de commando‑wachtrij van de hoofdverbinding gestuurd. Een voorbeeld van bewerkingen in een extra thread zonder een nieuwe verbinding te creëren; alle transacties worden via de hoofdverbinding uitgevoerd:

b. Wanneer de gebruiker een methode uitvoert om een nieuwe verbinding te maken voor een extra thread, wordt deze thread geblokkeerd totdat de quotawaarde voor nieuwe verbindingen wordt aangepast om een nieuwe verbinding toe te staan. Vervolgens wordt de nieuwe verbinding aangemaakt. Deze verbinding wordt ingesteld als standaardverbinding voor alle bewerkingen in deze thread. Nadat alle bewerkingen in deze thread zijn voltooid, moet de verbinding worden vrijgegeven. Gebruik de methode CredentialsByHostClient.CreateConnection om nieuwe verbindingen te maken. Deze methode retourneert een object dat de IDisposable‑interface implementeert. Om de verbinding vrij te geven, moet de Dispose‑methode worden aangeroepen. Het aanmaken en vrijgeven van de verbinding moet worden uitgevoerd binnen de thread waarin de e‑mailbewerkingen worden uitgevoerd. Proberen een nieuwe verbinding te maken in de thread waarin de e‑mailclient is gecreëerd leidt tot een fout, omdat deze thread op dat moment niet kan worden gebruikt voor het aanmaken van een nieuwe verbinding. Ook kan er geen nieuwe verbinding worden aangemaakt wanneer MaxConnectionsPerServer = 1. Een codevoorbeeld van het creëren van een nieuwe verbinding in een extra thread:

Aanbevelingen

Het is het vermelden waard dat als de gebruiker alle commando’s naar de hoofdverbinding stuurt, er een situatie kan ontstaan waarin commando’s van verschillende threads worden vermengd. De gebruiker moet begrijpen welke commando’s afhankelijk zijn van hun volgorde en maatregelen nemen voor de synchronisatie van dergelijke commando’s. Het is ook noodzakelijk om de mogelijkheid te overwegen om commando’s in verschillende sessies (IMAP/POP3) uit te voeren. De meest tijdrovende bewerkingen, zoals FetchMessage, AppendMessage en Send, hebben waarschijnlijk zin om uit te voeren met een nieuwe thread en een nieuwe verbinding. Maar snelle bewerkingen zoals Delete hebben zin om uit te voeren met de hoofdverbinding. Houd er rekening mee dat het initialiseren van een nieuwe verbinding al een tijdrovende bewerking is.