C#Mailkit imap客户端空闲模式未取消

时间:2019-06-13 16:33:53

标签: c# imap mailkit

当收到新电子邮件时,将运行闲置停止方法,并在其中显示Imap客户端当前正在另一个线程中进行处理。我认为是因为闲置命令仍在后台线程中运行? 即使我调用thread.Join()方法,也不会结束。我在这里呆了很长时间,MailKit github上的示例演示仅显示了如何在手动用户输入(例如Console.ReadKey())的帮助下进行处理。 我敢肯定,我缺少一些要点,或者代码有重大缺陷,但是我已经搜索了很多次答案,除了github示例以外,似乎没有任何重要结果

协议记录器,当空闲启动并收到消息后,直到出现异常为止

File "/home/wangxu/pyApp/flaskApp/bin/gunicorn", line 11, in <module>
sys.exit(run())
File "/home/wangxu/pyApp/flaskApp/lib/python3.6/site-packages/gunicorn/app/wsgiapp.py", line 61, in run
WSGIApplication("%(prog)s [OPTIONS] [APP_MODULE]").run()
File "/home/wangxu/pyApp/flaskApp/lib/python3.6/site-packages/gunicorn/app/base.py", line 223, in run
super(Application, self).run()
File "/home/wangxu/pyApp/flaskApp/lib/python3.6/site-packages/gunicorn/app/base.py", line 72, in run
Arbiter(self).run()
File "/home/wangxu/pyApp/flaskApp/lib/python3.6/site-packages/gunicorn/arbiter.py", line 203, in run
self.manage_workers()
File "/home/wangxu/pyApp/flaskApp/lib/python3.6/site-packages/gunicorn/arbiter.py", line 545, in manage_workers
self.spawn_workers()
File "/home/wangxu/pyApp/flaskApp/lib/python3.6/site-packages/gunicorn/arbiter.py", line 616, in spawn_workers
self.spawn_worker()
File "/home/wangxu/pyApp/flaskApp/lib/python3.6/site-packages/gunicorn/arbiter.py", line 583, in spawn_worker
worker.init_process()
File "/home/wangxu/pyApp/flaskApp/lib/python3.6/site-packages/gunicorn/workers/base.py", line 134, in init_process
self.run()
File "/home/wangxu/pyApp/flaskApp/lib/python3.6/site-packages/gunicorn/workers/sync.py", line 124, in run
self.run_for_one(timeout)
File "/home/wangxu/pyApp/flaskApp/lib/python3.6/site-packages/gunicorn/workers/sync.py", line 68, in run_for_one
self.accept(listener)
File "/home/wangxu/pyApp/flaskApp/lib/python3.6/site-packages/gunicorn/workers/sync.py", line 30, in accept
self.handle(listener, client, addr)
File "/home/wangxu/pyApp/flaskApp/lib/python3.6/site-packages/gunicorn/workers/sync.py", line 135, in handle
self.handle_request(listener, req, client, addr)
File "/home/wangxu/pyApp/flaskApp/lib/python3.6/site-packages/gunicorn/workers/sync.py", line 176, in handle_request
respiter = self.wsgi(environ, resp.start_response)
File "/home/wangxu/pyApp/flaskApp/lib/python3.6/site-packages/flask/app.py", line 2328, in __call__
return self.wsgi_app(environ, start_response)
File "/home/wangxu/pyApp/flaskApp/lib/python3.6/site-packages/flask/app.py", line 2311, in wsgi_app
response = self.full_dispatch_request()
File "/home/wangxu/pyApp/flaskApp/lib/python3.6/site-packages/flask/app.py", line 1832, in full_dispatch_request
rv = self.dispatch_request()
File "/home/wangxu/pyApp/flaskApp/lib/python3.6/site-packages/flask/app.py", line 1818, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/home/wangxu/pyApp/flaskApp/app.py", line 10, in hello
s = traceback.print_stack()

开始闲置的方法

        KStream<String, byte[]> stream = streamsBuilder.stream(topic);

        stream.selectKey((k, v) -> {
            Map<String, String> headers = this.getHeaders(v);
            return ParserService.getFieldValue(headers, "api_message_id");
        })
                .groupByKey()
                .aggregate(() -> new byte[]{}, (aggKey, newValue, aggValue) -> {
                    byte[] c = new byte[aggValue.length + newValue.length];
                    System.arraycopy(aggValue, 0, c, 0, aggValue.length);
                    System.arraycopy(newValue, 0, c, aggValue.length, newValue.length);
                    return c;
                })
                .toStream()
                .foreach((k, v) -> {
                    Map<String, String> headers = this.getHeaders(v);
                    parserService.processGroupedMessage(getHeaders(v));
                });

与闲置有关的声明

S: * OK [UIDNEXT 21641] Predicted next UID.
S: * OK [HIGHESTMODSEQ 881089]
S: A00000006 OK [READ-WRITE] INBOX selected. (Success)
C: A00000007 IDLE
S: + idling
S: * 21512 EXISTS
C: DONE

停止闲置方法

        IdleClient.Inbox.MessageExpunged += OnMessageExpunged;
        IdleClient.Inbox.CountChanged += OnInboxCountChanged;

        ImapToken = new CancellationTokenSource();
        SetTokenValues(ImapToken.Token);

        ImapToken.Token.ThrowIfCancellationRequested();
        ImapThreadInfo = Helpers.InBackgroundThread(ImapIdleLoop, UniqueAccountId, true);

空闲循环方法

    private (int, Thread) ImapThreadInfo;
    private CancellationToken CancellationToken { get; set; }
    private CancellationToken DoneToken { get; set; }
    private CancellationTokenSource ImapToken { get; set; }
    private CancellationTokenSource Timeout { get; set; }


    private bool IsCancellationRequested => CancellationToken.IsCancellationRequested || DoneToken.IsCancellationRequested;
    private readonly object Mutex = new object();

    private void CancelTimeout() {
        lock (Mutex) {
            Timeout?.Cancel();
        }
    }

    private void SetTimeoutSource(CancellationTokenSource source) {
        lock (Mutex) {
            Timeout = source;

            if (Timeout != null && IsCancellationRequested) {
                Timeout.Cancel();
            }
        }
    }

    private void SetTokenValues(CancellationToken doneToken, CancellationToken cancellationToken = default) {
        CancellationToken = cancellationToken;
        DoneToken = doneToken;
        doneToken.Register(CancelTimeout);
    }

1 个答案:

答案 0 :(得分:0)

问题在于您正在等待线程从ImapClient的事件回调中加入,这意味着您正在阻止ImapClient继续运行。

解决方案是:不要这样做。

在IMAP命令处理器仍在处理服务器响应时,会发出MailKit的IMAP事件,因此您不能在这些事件处理程序中的同一ImapClient上调用更多命令。

您需要做的是在程序中实现某种命令队列,然后在CountChanged事件处理程序(或您正在处理的任何处理程序)中,将下一个命令排队,以在当前命令后调用一次完成。

一种简单的方法是将System.Threading.Tasks.Task保留在事件处理程序可以访问它并可以执行的位置:

task = task.ContinueWith (...);

这是实现命令队列的简单方法。