С# асинхронно отправляет несколько писем с токеном отмены

Я пытаюсь отправить несколько сообщений с помощью SendMailAsync, и я пытаюсь передать токен отмены, чтобы прервать процесс отправки, когда была запрошена отмена.

static void Send(SmtpClientFactory factory, IEnumerable<MailMessage> messages)
{
    Task.WaitAll(SendAsync(factory, messages, CancellationToken.None));
}

static async Task SendAsync(SmtpClientFactory factory, IEnumerable<MailMessage> messages, CancellationToken token)
{
    await Task.Run(async () =>
    {
        using (SmtpClient smtpClient = factory())
        {
            foreach (MailMessage message in messages)
            {
                await smtpClient.SendMailAsync(message).ConfigureAwait(false);
            }
        }
    }, token);
}

Когда я использую неасинхронную отправку, она попадает в тупик. Как я могу отправить свое сообщение синхронно и использовать токен отмены для отмены, когда время истекло?


person Mohamed Nuur    schedule 12.06.2018    source источник
comment
Вам не нужно запускать метод в Task.Run, поскольку SmtpClient является асинхронным.   -  person Richard Szalay    schedule 12.06.2018


Ответы (1)


Вы можете комбинировать CancellationToken.Register и SmtpClient.SendAsyncCancel, чтобы делать то, что вам нужно:

static async Task SendAsync(SmtpClientFactory factory, IEnumerable<MailMessage> messages, CancellationToken token)
{
    await Task.Run(async () =>
    {
        using (SmtpClient smtpClient = factory())
        {
            foreach (MailMessage message in messages)
            {
                token.ThrowIfCancellationRequested();

                using (token.Register(() => smtpClient.SendAsyncCancel()))
                {
                    await smtpClient.SendMailAsync(message).ConfigureAwait(false);
                }
            }
        }
    }, token);
}

Имейте в виду, что SmtpClient официально объявлен устаревшим. :

SmtpClient и его сеть типов плохо спроектированы, мы настоятельно рекомендуем вам использовать https://github.com/jstedfast/MailKit и https://github.com/jstedfast/MimeKit вместо этого

MailKit не только потокобезопасен, но и гораздо более гибок с точки зрения создания сообщений, а также поддерживает CancellationToken при загрузке!

person Richard Szalay    schedule 12.06.2018
comment
Это по-прежнему вызывает взаимоблокировку в веб-приложении ASP.NET (при использовании Task.WaitAll с методом Send в моем вопросе). - person Mohamed Nuur; 12.06.2018
comment
Приводит ли ваш WaitAll к параллельному использованию одного и того же экземпляра SmtpClient? Это не поддерживается - person Richard Szalay; 13.06.2018
comment
@RichardSzalay - вы можете перенести операции с токенами за пределы for; не нужно делать их для каждой итерации? - person sellotape; 13.06.2018
comment
@RichardSzalay нет, разные экземпляры. - person Mohamed Nuur; 19.06.2018