Проблемы с использованием библиотеки MailKit

189
21 апреля 2018, 16:52

Использую библиотеку 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);
    }
}
Answer 1

Причина ошибки

Метод IdleAsync асинхронный, возвращающий Task.

Не дожидаясь выполнения асинхронной операции, вы пытаетесь использовать синхронную версию метода, используя специальное свойство для синхронизации синхронных операций SyncRoot. Но ваша предыдущая асинхронная операция еще не завершилась, другой поток работает с клиентом. Соответственно и вы и получаете данную ошибку.

Как правильно работать с клиентом?

Согласно документации, метод IdleAsync переводит клиента в состояние IDLE(паузы). Чтобы клиента вывести из состояния IDLE(паузы) и завершить асинхронную операцию, вам необходимо для токена currentOperationTioken, который вы передали в качестве параметра функции, вызвать метод Сancel. Тогда ваша асинхронная задача "начнет завершаться".

Поэтому у вас должен быть следующий алгоритм работы:
1. Вызываете асинхронный метод IdleAsync(без await), сохраняя в переменной возвращаемый Task
2. Когда вам необходимо что-то прочитать, сделать, обработать то, что получили от сервера, вы вызываете Cancel для токена, который вы передали в функцию в качестве параметра.
3. Вызываете await для Task, который вернул вам метод IdleAsync, чтобы ТОЧНО дождаться завершения асинхронной операции .
4. Выполняете обработку данных.
5. После всей обработки, снова вызываете метод IdleAsync, переводя клиента в состояние IDLE, чтобы продолжить получать события от сервера.
И так далее.

Стоит еще отметить, что отмененный токен нельзя использовать повторно. Необходимо создавать все время новый токен с помощью классаCancellationTokenSource для каждого вызова IdleAsync

READ ALSO
Где я могу задать ключи для компиляции в Visual Studio?

Где я могу задать ключи для компиляции в Visual Studio?

Прочитал на хабре, что можно уменьшить время компиляции, за счет ключа параллельной компиляции /MPВ настройках не вижу таких пунктов

237
Проблема ObservableCollection<T> Deserialize

Проблема ObservableCollection<T> Deserialize

Не могу разобраться с проблемой десериализации колекции объектов

173
Не сложная программа для разметки с языком css [требует правки]

Не сложная программа для разметки с языком css [требует правки]

Нужно что-нибудь не сложное по типу Lazarus

230
Подсветка текущего пункта меню

Подсветка текущего пункта меню

Задача стоит в том чтобы текущий пункт меню подсвечивая, но что-то не работает

215