user1692333
user1692333

Reputation: 2597

How to create new python thread with classes?

On the basis of the SMTPD component I wrote my own SMTP server with authorization and etc... The last thing I need to do is add message parsing in to new thread when it comes.

So my component consists of two classes: SMTPServer and SMTPChannel. It works perfectly in one thread like this:

def handle_accept(self):
    pair = self.accept()

    if pair is not None:
        conn, addr = pair

        logging.info('Incoming connection from ' + str(addr))

        channel = SMTPChannel(config, conn, addr)

Log look like this:

kiril@kiril-X501A1 ~/www/py_email $ python SMTPServer.py
[2015-05-27 11:15:57] [MainThread] INFO: Server started at 192.168.0.103:1026
[2015-05-27 11:16:46] [MainThread] INFO: Incoming connection from ('192.168.0.46', 52423)
[2015-05-27 11:16:46] [MainThread] DEBUG: <-- '220 SMTP Server 0.1 ESMTP'
[2015-05-27 11:16:46] [MainThread] DEBUG: --> EHLO : '*******'
[2015-05-27 11:16:46] [MainThread] DEBUG: <-- '250-SMTP Server 0.1 ESMTP Hello *******\n250-SIZE 20480000\n250 AUTH PLAIN LOGIN CRAM-MD5'
[2015-05-27 11:16:47] [MainThread] DEBUG: --> AUTH : 'PLAIN AGFkbWluADEyMzQ='
[2015-05-27 11:16:47] [MainThread] INFO: ('192.168.0.46', 52423) successfully authenticated
[2015-05-27 11:16:47] [MainThread] DEBUG: <-- '235 Authentication successful.'
[2015-05-27 11:16:47] [MainThread] DEBUG: --> MAIL : 'FROM:<reply@*******>'
[2015-05-27 11:16:47] [MainThread] DEBUG: <-- '250 Ok'
[2015-05-27 11:16:47] [MainThread] DEBUG: --> RCPT : 'TO:<destination@*******>'
[2015-05-27 11:16:47] [MainThread] DEBUG: <-- '250 Ok'
[2015-05-27 11:16:47] [MainThread] DEBUG: --> DATA
[2015-05-27 11:16:47] [MainThread] DEBUG: <-- '354 End data with <CR><LF>.<CR><LF>'
[2015-05-27 11:16:47] [MainThread] DEBUG: <-- '250 Ok'
[2015-05-27 11:16:47] [MainThread] DEBUG: --> QUIT
[2015-05-27 11:16:47] [MainThread] DEBUG: <-- '221 Bye'
[2015-05-27 11:16:47] [MainThread] INFO: Incoming connection from ('192.168.0.46', 52422)
[2015-05-27 11:16:47] [MainThread] DEBUG: <-- '220 SMTP Server 0.1 ESMTP'
[2015-05-27 11:16:47] [MainThread] DEBUG: --> EHLO : '*******'
[2015-05-27 11:16:47] [MainThread] DEBUG: <-- '250-SMTP Server 0.1 ESMTP Hello *******\n250-SIZE 20480000\n250 AUTH PLAIN LOGIN CRAM-MD5'
[2015-05-27 11:16:48] [MainThread] DEBUG: --> AUTH : 'PLAIN AGFkbWluADEyMzQ='
[2015-05-27 11:16:48] [MainThread] INFO: ('192.168.0.46', 52422) successfully authenticated
[2015-05-27 11:16:48] [MainThread] DEBUG: <-- '235 Authentication successful.'
[2015-05-27 11:16:48] [MainThread] DEBUG: --> MAIL : 'FROM:<reply@*******>'
[2015-05-27 11:16:48] [MainThread] DEBUG: <-- '250 Ok'
[2015-05-27 11:16:48] [MainThread] DEBUG: --> RCPT : 'TO:<destination@*******>'
[2015-05-27 11:16:48] [MainThread] DEBUG: <-- '250 Ok'
[2015-05-27 11:16:48] [MainThread] DEBUG: --> DATA
[2015-05-27 11:16:48] [MainThread] DEBUG: <-- '354 End data with <CR><LF>.<CR><LF>'
[2015-05-27 11:16:48] [MainThread] DEBUG: <-- '250 Ok'
[2015-05-27 11:16:48] [MainThread] DEBUG: --> QUIT
[2015-05-27 11:16:48] [MainThread] DEBUG: <-- '221 Bye'

But when I add threads like this:

def handle_accept(self):
    pair = self.accept()

    if pair is not None:
        conn, addr = pair

        logging.info('Incoming connection from ' + str(addr))

        new_thread = threading.Thread(target=SMTPChannel, args=(config, conn, addr,))
        new_thread.start()

The log looks like this:

kiril@kiril-X501A1 ~/www/py_email $ python SMTPServer.py
[2015-05-27 11:23:29] [MainThread] INFO: Server started at 192.168.0.103:1026
[2015-05-27 11:23:49] [MainThread] INFO: Incoming connection from ('192.168.0.46', 52414)
[2015-05-27 11:23:49] [Thread-1  ] DEBUG: <-- '220 SMTP Server 0.1 ESMTP'
[2015-05-27 11:23:49] [MainThread] DEBUG: --> EHLO : '*****'
[2015-05-27 11:23:49] [MainThread] DEBUG: <-- '250-SMTP Server 0.1 ESMTP Hello ********\n250-SIZE 20480000\n250 AUTH PLAIN LOGIN CRAM-MD5'
[2015-05-27 11:23:49] [MainThread] DEBUG: --> AUTH : 'PLAIN AGFkbWluADEyMzQ='
[2015-05-27 11:23:49] [MainThread] INFO: ('192.168.0.46', 52414) successfully authenticated

As you can see Thread-1 fires only once, then all messages comes back to main thread... May be it has something to deal with class extending as class MerlinSMTPServer(asyncore.dispatcher): and class SMTPChannel(asynchat.async_chat):

Upvotes: 0

Views: 71

Answers (1)

abarnert
abarnert

Reputation: 365707

You can't mix asyncore and threads like this. The whole point of an asyncore/asynchat application is that there's an event loop around select that handles all of your sockets, and makes calls to the various handlers. That all happens on a single thread. See the intro to the asyncore documentation, which explains this.

Starting a new asynchat channel on another thread doesn't do any good. That just means that it registers itself with the event loop from another thread. (Which isn't actually thread-safe, so you're just getting lucky if it works.) After that, the event loop, and the dispatching to the channel's handlers, all happens on the main thread.

If you want to use multi-threading, don't use asyncore. If you want to use asyncore, don't use multi-threading.

Upvotes: 1

Related Questions