如何在 MailKit C# 方法中使用锁定对象并使用异步?

问题描述 投票:0回答:1

如果您使用某些 MailKit 方法并对 API 进行一些并发访问,您将收到如下所示的

InvalidOperationException
异常:

ImapClient 当前正忙于处理另一个线程中的命令。锁定 SyncRoot 属性以正确同步您的线程。

您通常会这样做,正如错误消息所建议的那样:

lock (imapClient.SyncRoot)
{
    imapClient.GetFolder(/* ... */)
}

但是,如果你这样做:

lock (imapClient.SyncRoot)
{
    await imapClient.GetFolderAsync(/* ... */)
}

这不起作用/编译,因为CS1996直接链接),因为:

await
表达式不能在lock语句的范围内使用。

我已经阅读并发现了有关该主题的许多问题,但我仍然不清楚最好的解决方案。如果这从根本上不兼容,那么应该如何使用异步 Mailkit API?如果这从根本上与该方法不兼容,为什么它会建议

.SyncRoot

c# .net-core async-await locking mailkit
1个回答
0
投票

我发现的唯一易于实施的解决方案是使用

SemaphoreSlim
就像这里建议的那样。这也是也是问题的通用解决方案

它可以例如作为

ImapClient
:

的扩展来实现
/// <summary>
/// Using multiple instances of <see cref="MailKit.Net.Imap.ImapClient"/> to connect to the same mailbox at the same time is very likely to cause issues.
/// This is because an IMAP server simply refuses to take new commands when it is currently busy executing another command. To make a single ImapClient instance
/// accessible throughout the app, to be used by multiple concurrent Tasks, we can restrict access to it by using a semaphore that can be locked and released
/// before and after using the ImapClient.
/// </summary>
public interface IImapClientWithSemaphore : IImapClient
{
    /// <summary>
    /// <b>Always</b> wait for the semaphore before executing any relevant command. See <see cref="IImapClientWithSemaphore"/> for a detailed explanation on why.
    /// </summary>
    public SemaphoreSlim Semaphore { get; }
}

然后你就可以这样做:

await imapClient.Semaphore.WaitAsync(CancellationToken.None);

try
{
    await imapClient.GetFolderAsync(/* ... */)
}
finally
{
    imapClient.Semaphore.Release();
}
© www.soinside.com 2019 - 2024. All rights reserved.