Использую библиотеку MailKit. Вот код
currentOperationTioken = new CancellationTokenSource();
client = new ImapClient();
client.Connected += Client_Connected;
client.Authenticated += Client_Authenticated;
client.Connect("imap", 993, true);
client.Authenticate("login", "pass");
inbox = client.Inbox.GetSubfolder("some");
inbox.Open(FolderAccess.ReadOnly);
inbox.CountChanged += Inbox_CountChanged;
client.IdleAsync(currentOperationTioken.Token);
lock (client.SyncRoot)
{
foreach (var uid in inbox.Search(SearchQuery.NotSeen))
{
//var message = inbox.GetMessage(uid);
Console.WriteLine(inbox.GetMessage(uid).Subject);
}
Console.WriteLine(inbox.Count);
}
На строке foreach (var uid in inbox.Search(SearchQuery.NotSeen)) ловлю вот такую ошибку :"The ImapClient is currently busy processing a command in another thread. Lock the SyncRoot property to properly synchronize your threads."
С потоками у меня все плохо, помогите разобраться пожалуйста как правильно сделать. Код, который находится в lock будет отрабатывать в событии Inbox_CountChanged, если я правильно понимаю, то под капотом IdleAsync, какой то вечный цикл, потому что у объекта client есть метод Idle, после которого код останавливается на мониторинге директории(Методом тыка проверял). Уже думаю над вариантов делать два объекта ImapClient, один для мониторинга директории на новые сообщения, второй для вычитки это директории, но мне это не нравится.
Пробовал сделать с использованием await. Выполнение останавливается на строке вызова асинхронного метода(Я так думаю), так как дальше ничего не происходит.
private async static void connectImap()
{
currentOperationTioken = new CancellationTokenSource();
CancellationTokenSource currentOperationTioken1 = new CancellationTokenSource();
client.Connected += Client_Connected;
client.Authenticated += Client_Authenticated;
client.Connect("imap", 993, true);
client.Authenticate("login", "pass");
inbox = client.Inbox.GetSubfolder("some");
inbox.Open(FolderAccess.ReadOnly);
inbox.CountChanged += Inbox_CountChanged;
await client.IdleAsync(currentOperationTioken.Token);
lock (client.SyncRoot)
{
foreach (var uid in inbox.Search(SearchQuery.NotSeen))
{
//var message = inbox.GetMessage(uid);
Console.WriteLine(inbox.GetMessage(uid).Subject);
}
Console.WriteLine(inbox.Count);
}
}
Причина ошибки
Метод IdleAsync асинхронный, возвращающий Task.
Не дожидаясь выполнения асинхронной операции, вы пытаетесь использовать синхронную версию метода, используя специальное свойство для синхронизации синхронных операций SyncRoot. Но ваша предыдущая асинхронная операция еще не завершилась, другой поток работает с клиентом. Соответственно и вы и получаете данную ошибку.
Как правильно работать с клиентом?
Согласно документации, метод IdleAsync переводит клиента в состояние IDLE(паузы). Чтобы клиента вывести из состояния IDLE(паузы) и завершить асинхронную операцию, вам необходимо для токена currentOperationTioken, который вы передали в качестве параметра функции, вызвать метод Сancel. Тогда ваша асинхронная задача "начнет завершаться".
Поэтому у вас должен быть следующий алгоритм работы:
1. Вызываете асинхронный метод IdleAsync(без await), сохраняя в переменной возвращаемый Task
2. Когда вам необходимо что-то прочитать, сделать, обработать то, что получили от сервера, вы вызываете Cancel для токена, который вы передали в функцию в качестве параметра.
3. Вызываете await для Task, который вернул вам метод IdleAsync, чтобы ТОЧНО дождаться завершения асинхронной операции .
4. Выполняете обработку данных.
5. После всей обработки, снова вызываете метод IdleAsync, переводя клиента в состояние IDLE, чтобы продолжить получать события от сервера.
И так далее.
Стоит еще отметить, что отмененный токен нельзя использовать повторно. Необходимо создавать все время новый токен с помощью классаCancellationTokenSource для каждого вызова IdleAsync
Апостиль в Лос-Анджелесе без лишних нервов и бумажной волокиты
Основные этапы разработки сайта для стоматологической клиники
Продвижение своими сайтами как стратегия роста и независимости