1

I'm using a non-blocking asynchronous threading websockets protocol (Tornado) in python 3. I'm using a dictionary very conservatively in the global level, but it can be updated by any number of threads (basically when a new user connects, their socket is mapped to an object via the dictionary). I've made a basic system to deal with concurrency... but it's not any official method. Since the sockets are unique, I don't think I should have any problem with people trying to assign values to the same socket at the same time, but I was wondering if with scale, there might be another problem, such as trying to get something from the dictionary when it's being resized. For reference, here's the "concurrency-fix" I came up with.

class Routing_Table:

    def set(self, key, value):
        self.blocked = self.blocked + 1
        self.table[key] = value
        self.blocked = self.blocked - 1

    def get(self, key):
        while(self.blocked > 0):
            pass
        return self.table[key]

    def __init__(self):
        self.table = {}
        self.blocked = 0

EDIT: Also, do you think I should add a method similar to set for deleting entries?

Cœur
  • 37,241
  • 25
  • 195
  • 267
SwiftCore
  • 659
  • 9
  • 17
  • 3
    You are trying to reinvent Locks, RLocks and Semaphores. You can look these terms up in the python documentation. – Hyperboreus Aug 01 '13 at 23:55
  • Why do you think you need this? Isn't Tornado Event Driven? A handler is called when an event fires, the handler runs until it is does, and then the program can handle another event. – Eric Urban Aug 01 '13 at 23:56
  • @Hyperboreus -- I don't know much about locks, but I know that I can't use them because Tornado is non-blocking. – SwiftCore Aug 01 '13 at 23:57
  • What has non-blocking behaviour to do with locks? – Hyperboreus Aug 01 '13 at 23:58
  • @Hyperboreus Doesn't that mean that the threads can't lock? I don't know, that's what someone told me. I assume non-blocking means multiple threads fire at once automatically and that behavior cannot be changed. Also I found nothing on locks in the Tornado documentation, and my threads (generally) are tornado threads. – SwiftCore Aug 02 '13 at 00:00
  • Also, if it worked at all, that `while self.blocked > 0: pass` would be _exactly_ as blocking as waiting on a lock! – abarnert Aug 02 '13 at 00:01
  • The Python interpreter itself is blocking, google "python gil" to see lots of discussion. – Codie CodeMonkey Aug 02 '13 at 00:01
  • @CodieCodeMonkey Quick side note. Is CPython the same thing as Cython? Because every GIL things I've seen has mentioned CPython. – SwiftCore Aug 02 '13 at 00:04
  • 1
    @SwiftCore: No, CPython is the most common Python interpreter. Cython is a set of tools for building extension modules for CPython. – abarnert Aug 02 '13 at 00:05
  • 1
    @SwiftCore, I agree with abamert, but you can also use Cython to compile python code, as I understand it. – Codie CodeMonkey Aug 02 '13 at 00:07
  • @abarnert: Wow! That's because it's written in C right? (I was trying to look at the source to figure out if this were a problem). So this whole thread is silly and I should see one of the [many other](http://stackoverflow.com/questions/1312331/using-a-global-dictionary-with-threads-in-python) threads on gil? – SwiftCore Aug 02 '13 at 00:11
  • @SwiftCore: Yes, it's called CPython because it's written in C (and because they needed a name for it retroactively to distinguish it from JPython and the other later interpreter implementations). Meanwhile, Cython doesn't compile Python code… but it compiles code in a Python-like language (itself called Cython), which is often good enough. – abarnert Aug 02 '13 at 00:16
  • One last thing: Python threading is good when your threads spend almost all of their time waiting on I/O or locks. If they're actually running code all the time, more threads just means everything runs slower (because of the GIL), so you really need separate _prcoesses_ instead. Tornado presumably does a bunch of smart stuff to try to balance things out nicely, but it has to make some assumptions about what _your_ code is going to do with its threads… – abarnert Aug 02 '13 at 00:19
  • Oh, and see [this post](https://github.com/facebook/tornado/wiki/Threading-and-concurrency) on the Tornado wiki, which explains most of what the comments and answers here did (although not with all the details). – abarnert Aug 02 '13 at 00:20

1 Answers1

2

If you want to do anything in a thread-safe manner, the basic idea is:

class ConcurrentThingy:
    def __init__ (self):
        self.lock = Lock () # or RLock () if you want it to be reentrant    

    def concurrentlyAccessedMethod (self, *args, **kwargs):
        with self.lock: doMeanStuff ()

Your class could look something like this:

class Routing_Table:
    def set (self, key, value):
        with self.lock: self.table[key] = value

    def get(self, key):
        with self.lock: return self.table[key]

    def __init__(self):
        self.table = {}
        self.lock = Lock ()
Hyperboreus
  • 31,997
  • 9
  • 47
  • 87
  • Accessing and settings dictionary items isn't guaranteed atomic. The `STORE_SUBSCR` may only be one opcode, but it can potentially call any arbitrary function—in fact, it _must_ do so, in order to hash your key. – abarnert Aug 02 '13 at 00:05
  • I'm sorry, I haven't taken an operating systems course yet (going to take the [OCW](http://ocw.mit.edu) one as soon as I finish algorithms), but I thought in order to lock you had to have control of the threads? So if Tornado is making the threads, then I can't do this? If not, I'll do this as it's cleaner than writing the code myself. – SwiftCore Aug 02 '13 at 00:07
  • @SwiftCore: If Tornado is making the the threads, you _can_ do this… it's just that you could end up breaking some assumptions that Tornado makes. – abarnert Aug 02 '13 at 00:08
  • @SwiftCore and what @hyperboreus fails to mention is you should always use ``with `` in case of exceptions the lock will still release – Eiyrioü von Kauyf Aug 02 '13 at 00:11
  • @SwiftCore Please look at my edit. Eiyiroü von Kauyf, abarnert I hope the patched class I provided is indeed thread sage, otherwise please correct me. – Hyperboreus Aug 02 '13 at 00:11
  • @EiyrioüvonKauyf If I am not mistaking, I am indeed using `with`. – Hyperboreus Aug 02 '13 at 00:11
  • @Hyperboreus: Your "unconfirmed guess" at the top is still wrong. I believe your `Routing_Table` is, however, thread-safe (and I think it already was before the edits). – abarnert Aug 02 '13 at 00:12
  • @abarnert Thank you. I removed my bad guess. – Hyperboreus Aug 02 '13 at 00:13
  • I'm saying you never explained why a ``with`` randomly popped up; not if you used it or not – Eiyrioü von Kauyf Aug 02 '13 at 00:13
  • Well, the original question was largely if this is _going to be_ a concurrency problem, but regardless, this should prevent it and get rid of any amateur locks so I'm going to mark it as the right answer. Thanks for your help. – SwiftCore Aug 02 '13 at 00:15
  • Thank you. Maybe my answer can help you in the future, when you need to do more complex processing in a thread-safe manner. – Hyperboreus Aug 02 '13 at 00:17
  • also at that point you were not using it; you added it after my comment; – Eiyrioü von Kauyf Aug 02 '13 at 06:25