2

I'm using bottle with a cherrypy server to utilize multithreading. As I understand it this makes each request handled by a different thread. So given the following code:

from bottle import request, route

somedict = {}

@route("/read")
def read():
  return somedict


@route("/write", method="POST")
def write():
  somedict[request.forms.get("key")] = request.forms.get("value")

Would somedict be thread safe? What if a daemon thread were run to manage somedict, say it's a dictionary of active sessions and the daemon thread prunes expired sessions? If not would a simple locking mechinism suffice, and would I need to use it when reading, writing, and in the daemon thread, or just in the daemon thread?

Also as I understand it cherrypy is a true multithreaded server. Is there a more proper method I should use to impliment a daemon thread while using cherrypy as pythons threads are not true threads? I don't wish to delve much into the cherrypy environment preferring to stick with bottle for this project though, so if it involves moving away from bottle/migrating my app to cherrypy then it doesn't really matter for now. I'd still like to know though as I didn't see much in their documentation on threads at all.

kryptobs2000
  • 3,289
  • 3
  • 27
  • 30

3 Answers3

1

In your particular example, yes, the (single) dict assignment you perform is threadsafe.

somedict[request.forms.get("key")] = request.forms.get("value")

But, more generally, the proper answer to your question is: you will indeed need to use a locking mechanism. This is true if, for example, you make multiple updates to somedict while handling a single request, and you need them to be made atomically.

The good news is: it's probably as simple as a mutex:

from bottle import request, route
import threading

somedict = {}
somedict_lock = threading.Lock()

@route("/read")
def read():
    with somedict_lock:
        return somedict

@route("/write", method="POST")
def write():
    with somedict_lock:
        somedict[request.forms.get("key1")] = request.forms.get("value1")
        somedict[request.forms.get("key2")] = request.forms.get("value2")
ron rothman
  • 17,348
  • 7
  • 41
  • 43
0

I had originally answered that a dict is threadsafe, but on futher research, that answer was wrong. See here for a good explanation.

For a quick explanation, imagine two threads running this code at once:

d['k'] += 1

They might both read d['k'] at the same time, and thus instead of being incremented by 2, be incremented only by 1.

I don't think it's an issue of your application locking up, more of just some data being lost. If that's not acceptable, using threading.Lock is pretty easy, and doesn't add that much overhead.

Here's some good info on thread safety with CherryPy. You might also consider using something like gunicorn in place of CherryPy. It has a worker process model, so each somedict would be different for every process, so there would be no worry of thread-safety.

Community
  • 1
  • 1
korylprince
  • 2,969
  • 1
  • 18
  • 27
  • So using bultin threads and cherrypy somedict can be used to safely share data between requests and even daemon threads? That answers my question if so and makes this much easier, but it seems like magic to me. How is cherrypy able to share the dict in a thread safe manner within threads that I spawn within the application while base python requires locking for such behavior? Or perhaps as your first sentence implies my understanding about base python's behavior is simply incorrect as well? – kryptobs2000 Nov 08 '13 at 03:32
0

CherryPy is based on Python threads, so you should stay away from using it as an HTTP server only (and any other native HTTP server). I suggest that you go with uWSGI, which is multiprocess and thus doesn't have GIL issues. Since it is multiprocess, you won't be able to use simple thread-shared variables. You can use uWSGI's SharedArea though or any 3rd party data storage.

jwalker
  • 1,999
  • 16
  • 27