14 ms·
Why I went from Python to Go (and not node.js)
- zallarak 14y agoVery cool post. The code samples really make me want to try out Go when I get some time to do so. Thanks.
- zemo 14y agothanks for reading! I recommend starting with http://tour.golang.org/ http://tour.golang.org/. It's a very useful learning tool.
- realrocker 14y agoIn Go, the coroutines are in the same thread. There is a single thread here too(just like node.js). Coroutines are just multiplexed to the one main thread. Multi-Core Processing is handled by coroutine internals too i.e. there may or may not be more than one threads and even if there are more than one threads, they too will be multiplexed with the one main thread.
- 4ad 14y agoGoroutines running Go code are multiplexed onto a variable number of system threads (GOMAXPROCS, in the gc implementation), Goroutines running C code, for example via cgo, or when they call a blocking system call, have their own thread.
- realrocker 14y agoYes I get that. I just wanted to point out that a single threaded model is not necessarily a bad thing. It happens in GO too by default.
- strmpnk 14y agoI think that's more of a matter of scheduler still being immature. Erlang only added an SMP scheduler in R11 due to the complexities of managing internal state without sacrificing it's soft-realtime abilities. It was also only really reliable after R12. I'm sure Go will mature here and the defaults will evolve along with that. Whether that will take years or not is up for debate but at least it's a possibility and doesn't cost the programmer anything other than a recompile if they start with careful use of goroutines today.
- Jabbles 14y agoI believe the number of threads can be higher than GOMAXPROCS, but only GOMAXPROCS can be running at once. https://groups.google.com/d/topic/golang-nuts/bP6_KD51ed4/discussion https://groups.google.com/d/topic/golang-nuts/bP6_KD51ed4/di...
- aaronblohowiak 14y ago>In Go, the coroutines are in the same thread. .. until you set runtime.GOMAXPROCS(runtime.NumCPU())
- ak217 14y agoLots of sentiment, not much substance. Concurrency support is possible in Python, without gevent-style monkey patching (or callback madness). Have a look at concurrent.futures and http://www.dabeaz.com/coroutines/index.html http://www.dabeaz.com/coroutines/index.html. It really needs a lot more work before it's part of the language's DNA, though. Also, pypy needs much wider adoption as quickly as possible, to address the speed problems (and its STM branch holds huge potential). For me, Go's major shortcoming is its community's lack of focus on readability as compared to Python.
- peter_l_downs 14y ago+1 for dabeaz's coroutines presentation. It's really well done, and if you write Python daily / monthly / even yearly it's worth reading.
- zemo 14y ago>Lots of fluff, not much substance. that was deliberate. There has been a glut of "introduction to Go" blog posts, and the documentation for introductory Go from the Go Team is quite good, so I didn't feel the need to author another introductory post. Most of the stories about why people use Go are bigger, established companies, so I wanted to show how it was perceived by a different audience. I don't remember where I saw it, but someone talked about using Go in contrast with Java, and the overwhelming reaction was "that's cool but I'm a Python programmer, I'm curious what Python programmers think", so I was just trying to give it that angle. >Have a look at concurrent.futures but this is part of the problem: it's a library. Concurrency deserves language-level support. Adding concurrency at the library level is like adding an object system at the library level.
- outworlder 14y ago> but this is part of the problem: it's a library. Concurrency deserves language-level support. Adding concurrency at the library level is like adding an object system at the library level. Which is not a problem at all. Lisp and Scheme have been doing that since forever.
- bcoates 14y agoI'd like to see more detail about the real problem he was facing in python. It sounds like he mostly wants a non-blocking background job "I don’t want to set up another daemon, I just want to send some email in the background!" Why not just use Queue (thread-safe, waitable, built-in) and a background thread(pool)? There's an awful global lock on the actual execution of python code, but unless the problem is performance or contention worrying about it is premature.
- dignan 14y agoNot only that, but there's a multiprocess Queue class as well that is a drop in replacement for the threaded one. So if you are having problems with GIL contention (unlikely, but possible), you can still just as easily use multiple processes.
- cgh 14y agoExactly what I thought - "what about multiprocessing?" Doesn't that count as language-level concurrency support? You can fire off a background job to send email or whatever just as easily as in the Go example.
- milesvp 14y agoThis confused me as well, it felt like the author was thinking about the problem space all wrong. As soon as I read "As a Django developer, there wasn’t a straightforward and obvious way to just do things in the background on a page request" I started thinking, "um, django produces http responses, you can only respond once...". Clearly, from his gevents example, he's looking to do things that take time, and wants to do them as concurrently as possible, then send the whole response at once. But if he really doesn't want the user to wait, then this is not something you should be doing in a webserver anyways. You really should be building that into the front end javascript, and let it handle all the async data grabs from small, fast, cacheable web requests, then redraw the page as the data comes in.
- 14y ago
- srj55 14y ago"As a Django developer, there wasn’t a straightforward and obvious way to just do things in the background on a page request. People suggested I try Celery, but I didn’t like that option at all. A distributed task queue? What? I just want to do something in the background without making the user wait; I don’t need some super comprehensive ultimate computing machine. The whole notion that I would need to set up and configure one of these supported brokers made my spidey sense tingle" It's not really that hard. I just latch on to a broker that I'm already using elsewhere in my stack (Redis). Celery makes it super simple to run a command in the background.
- zemo 14y agowell, the complaint isn't entirely that it's hard, per se, but that there are many different ways to do things concurrently in Python, and that it presents too many choices to be made for the novice programmer. In Go, you just say `go myfunction()` and you're done. There's a lot of value in that.
- asksol 14y agoNote that Celery isn't just about what you describe here. A distributed task queue is beneficial in Go (or node.js) too. Async is one thing, distribution is another. Also web servers are often volatile environments (e.g. for tasks that must complete).
- erichocean 14y agoUntil, that is, you tackle a problem for which one machine isn't big enough, at which time you need to do all of the "real" solutions anyway, and `go myfunction()` buys you...nothing. There's way too much focus today on new languages that are designed to work on just one machine, and scale there – as if vertical scaling was the true problem we all face, when in fact, it's not. /sigh
- xyproto 14y agoGo is well suited for this too. Look at dozer at github.
- tocomment 14y agoIt it true there's no way to spawn a background task in Django without making the user wait?
- zemo 14y agoI haven't been using Django for a while, but I know there was no way built into the framework to do it. The complaint isn't really that it's too hard, it's that there's too many decisions to make, because it's not supported at the language level.
- asksol 14y agoSure you can do that (using threads/multiprocessing/greenlets). But usually you don't want to because it's hard to guarantee that the background task will complete without interruption.
- just2n 14y agoAnother gonatic? I'm immediately reminded of the days when everyone who liked D proclaimed it would overtake C++ and rule the world with terrible, contrived examples: "let me show you why my language is better than yours by completely misunderstanding how to solve a problem, then implementing that broken/overengineered solution in your language, then compare it to something my language's API can do for me, just so we can see how much simpler that bad solution is in my language!" The insight to async I/O is that this server, for this I/O bound task, will perform as well as your GOMAXPROCS example: require('http').createServer(function(req, res) { res.writeHead(200); res.end("hello world\n"); }).listen(8000); But it's a lot simpler. You don't need to bolt on parallelism when you don't actually need parallelism. There are valid reasons to use Go, and there are valid complaints against Node. I don't see any of either here.
- aaronblohowiak 14y ago>The insight to async I/O is that this server, for this I/O bound task, will perform as well as your GOMAXPROCS example: Will it? Luvit, which is implemented on Node's libuv outperforms Node in the trivial case because of higher lua performance (and lua->c interop performance).
- just2n 14y agoI'd be interested in a more in-depth look at why this might be the case, because in JS written such that V8 will optimize properly, it's generally faster.
- aaronblohowiak 14y agoIIRC, you can't tell v8 to keep memory in the same place (it can and does move things around as part of its gc).. this complicates sending strings to the socket, so you have to make a copy from the v8 world to some other world before initiating the async write.. i believe (suspect?) that lua does not have this limitation, so the extra work involved is not necessary.. this is based on old, vague notions so I may be off my rocker, but it is the best of my understanding. I would love a detailed look.
- mietek 14y agoDimissing Erlang and Haskell with a wave of the hand, while seriously considering node.js? Carry on, nothing to see here.
- zemo 14y agoah, so, I don't think I wrote that section particularly well. I hold Erlang and Haskell in very high regard; I was really trying to mock my own superiority complex, which I think is fairly common in the Python community. I guess what I was really trying to get at is that Go is verymuch an industry language. Haskell I would argue against being an industry language not because it can't be used in industry; that's not at all what I mean. I don't mean to compare them on their technical merits; what I mean to say is that Go has a broad appeal, in that it is welcoming to beginners, it has large corporate support, and that it is also technically capable. Of the programmers that I know, the Haskell programmers are typically capable of solving the hardest problems, but they also tend to be the most academic. The Erlang programmers typically build the most stable software, but I think Erlang is intimidating to less-experienced programmers (the same is true of Haskell, actually). That could have been written better. Thanks for the feedback.
- dons 14y agoYou're digging in. > Haskell I would argue against being an industry language FYI, the problems the Haskell community has been working on are things like scalability, performance and safety because they're critical to industrial problems. Toy approaches don't work at the scale we operate at -- you need real computer science. You want 1,000,000 Haskell threads in your app? You've got it. Want to write numerical models that compete with C++ code, in a fraction of the development time? Done. Want to guarantee the app won't crash? Solved. Run models over 10,000 cores? It happens. Because we put the work in. --- /me wanders back to a multi-million line Haskell codebase running systems in 25 countries, processing billions a year in financial transactions.
- zemo 14y agoThe point is that Go is more accessible than Haskell, not that it's better.
- ricardobeat 14y agoAh, code comparisons. I don't see much difference in the Go code vs Javascript, except for the extra comments and logging in the js. How about this? var cluster = require('cluster') , http = require('http') , os = require('os') if (cluster.isMaster) { os.cpus().forEach(cluster.fork) } else { http.createServer(function(req, res){ res.writeHead(200) res.end('Hello world') }).listen(8000) }
- geekstrada 14y agoThanks, his uneven code comparison was bothering me. The main difference I see is that the nodejs code is easier to read.
- enneff 14y agoOf course it's easier to read for you: you know JavaScript. I know both JS and Go and the Go version is much easier on my eyes - there's simply a lot less to it.
- deleted 14y ago[deleted]
- MatthewPhillips 14y agoos.cpus().forEach(cluster.fork) Should be: os.cpus().forEach(cluster.fork.bind(cluster));
- ricardobeat 14y agoThat's not needed. The API is designed so that you can do var fork = require('cluster').fork Cluster methods keep a reference to the main object inside the module, see https://github.com/joyent/node/blob/master/lib/cluster.js#L466 https://github.com/joyent/node/blob/master/lib/cluster.js#L4...
- enneff 14y agoYou should update the node.js docs if you consider the original JS snippet ugly, because that's where I pulled the original code sample from verbatim. In any case, it's still not an apples to apples comparison. The node.js example forks separate processes that share nothing, while in the Go example it all runs in one process so the HTTP handlers can communicate with each other via channels or other shared data structures.
- knodi 14y agoI'm in love with Go. I'm very close to deploying it in production.
- juddlyon 14y ago"... as a Python programmer, I was the member of an elite cabal of superhuman ultranerds, smarter than those childish Rails/JavaScript/PHP/whatever developers that couldn’t write a bubble sort or comprehend even basic algorithmic complexity, but more in touch with reality than the grey-bearded wizards of Lisp/Haskell/whatever that sat in their caves/towers/whatever solving contrived, nonexistent problems for people that don’t exist, or those insane Erlang programmers who are content writing sumerian cuneiform all day long." This made me laugh, thank you.
- IsTom 14y ago"Everyone less nerdy than me is stupid and everyone more nerdy than my is a no-life". I think it's pathetic, not funny.
- jimbokun 14y agoThat was the joke.
- jonathansizz 14y agoIt's actually kind of bizarre that so many Pythonistas actually have the kind of attitude that the article mocks, since Python is a thoroughly mediocre language. I can only attribute their patronizing disposition to a lack of genuine awareness of the broader programming world.
- Ideka 14y agosince Python is a thoroughly mediocre language. Care to elaborate?
- tikhonj 14y agoIs there anything to add? It's not very fast. It does not have any interesting abstraction. It doesn't push the envelope further. It doesn't allow any clever optimizations. It has a bunch of horrible gotchas (scoping, mutable default arguments, tuples of one element...). It isn't elegant--the grammar alone takes pages and it's just a loose collection of mostly orthogonal features with largely arbitrary (but, admittedly, readable) syntax. It is fairly verbose and writing most anything takes more than one line. It pretends to support functional programming, but only grudgingly and never well. It has OO support, but also rather half-heartedly compared to Self or Smalltalk. It has a completely arbitrary and unnecessary delineation between statements and expressions (this one really annoys me). In short, it's a language best define by what it isn't: it isn't bad, but it also isn't good. It isn't particularly elegant, but it isn't particularly ugly either. It isn't exceptionally expressive, but it's also no tarpit. It isn't very concise, but it isn't quite overly verbose. It is the very essence of plainness. If languages had a color, Python's would be a dull gray. An interesting thing to note is which communities do not like Python. For what it's worth, the least Python-endorsing site I've found is Lambda the Ultimate. That should definitely tell you something, although I suspect what it tells you depends entirely on your own preferences :P. Python really is nothing but mediocre and uninspiring. Sure, it isn't bad, but I would like a language that is actively good. The very interesting thing about all this is that, quite often, you need a mediocre language. So Python is actually a great fit for a bunch of startups--it's the modern lowest common denominator. But I--being deeply interested in programming languages--wouldn't want to work anywhere that settled on a language that way.
- stcredzero 14y agoIf someone created a debugging environment for Go based on a VM, which also let one recompile source from within the debugger then continue execution, then it would be, for all intents and purposes, as productive and immediate as the old Smalltalk environments. You'd have the same small-grained cycles of inspecting state, modifying code, rewinding the stack to the place of your choosing, then getting immediate feedback. Source code changes could be saved as log-structured patch files, which could then be thrown away or applied to the source tree as desired. One could also steal some ideas from the Smalltalk Change Log tool by adding similar editing, search, and filtering commands. With tools like this, one could recompile for "interpreted debug mode," have complete visibility and control of runtime state to debug a problem, then take the resulting patch file and apply it to the source tree. It would be a best of both worlds scenario -- all the enhanced debugging of an interpreted runtime with the type safety and speed of compiled code.
- taliesinb 14y agoInteractive coding is a very powerful way of working, it's one of the reasons I'm so productive in Mathematica. At a Go talk he did, I raised the idea with Russ Cox of having a "repl" package that would allow one to instrument a running program with a live REPL to do debugging and development on it. The reflect package is powerful enough to make some of that relatively straightforward, but one major problem is that Go can't construct new types at runtime. Another challenge would be dynamic linking of new code -- because the Go toolchain doesn't support dynamic linking there would seem to be no hope of say entering anonymous functions on the REPL. Unless one builds a full Go interpreter -- but who wants to be in the business of maintaining a fully compliant Go interpreter that can interoperate with the Go runtime? Still, calling existing functions and banging on variables would be pretty useful.
- stcredzero 14y ago> one major problem is that Go can't construct new types at runtime. The current implementations can't. I don't see any reason why one couldn't. In any case, I don't think that's such a big deal. One just goes from completely seamless interactive coding to mostly seamless interactive coding. > Unless one builds a full Go interpreter That is precisely what I was proposing. (Are you implying some sort of hard VM/interpreter dichotomy? I've met some people who implement VMs who think this is somewhat arbitrary.) >but who wants to be in the business of maintaining a fully compliant Go interpreter that can interoperate with the Go runtime? There would be no need to interoperate at all with the current Go runtime. One would have to have their own Go runtime, however. As an alternative, one could just target an emulator with no optimizations, then use debugging information and dirty tricks to map the new code with the old state in an entirely new process.
- it 14y agoIt would help a bit if the article included at least roughly equivalent Go code next to the Python code. The Go code is wordier, but maybe it takes less time to write because it doesn't require as many decisions (libraries etc.) as with Python. package main; import ( "fmt" "net" ) func main() { hosts := []string { "www.google.com", "www.example.com", "www.python.org" } c := make(chan string) for _, h := range(hosts) { go get_ip(h, c) } for i := 0; i < 3; i++ { fmt.Println(<-c) } } func get_ip(host string, c chan string) { addrs, err := net.LookupHost(host) if err != nil { fmt.Println("Host not found:", host) c <- host + ": <error>" return } c <- host + ": " + addrs[0] }
- zemo 14y agoThis is a very good example of the type of thing I'm talking about. Nobody prompted you to do so, but you gracefully handled the case of a DNS failure in your code, because the control path was obvious throughout. Go makes this type of error handling a topic very early on in the literature. I find this type of clarity when structuring concurrent code to be very helpful in minimizing subtle concurrency bugs.
- d0mine 14y agoOn error handling in Go vs. Python: Lack of exceptions means the deeper the call stack the higher the proportion of error handling code relative to a normal (non-exceptional) code path. For example, if you decide to refactor some code from a bigger function into its own smaller function then all error handling code have to be repeated twice (first in the child function then in the parent that calls it). It introduces unnecessary clutter and boiler-plate. Exceptions allows you to limit the error handling to two places: the place where you detect an error and the place where you are ready to handle it and not throughout the whole call stack. Language with a builtin garbage collection can (should) afford a builtin exception support. I like that Go excludes some language features on purpose but in the case of exceptions Python has its merits.
- antihero 14y agoI'd like to start porting a few of my little scripts to Go (that do pretty poor messy parallelism in Python), and I was wondering what a good resource/book type thing would be for people learning Go. Like, the equivalent of learn you some haskell or whatnot. Also some advice on "wtf library do I use for this". Is there some sort of Go package manager? How does all this shit work?
- zemo 14y agoI recommend starting with http://tour.golang.org/#1 http://tour.golang.org/#1. Yes, the Go tool comes with package-management capabilities. "go get labix.org/v2/mgo" will download the very excellent MongoDB driver mgo, for example.
- swasheck 14y agoBut then actually implementing something useful is cryptic and obscure. I tried figuring out how to connect to a postgresql server following the spaghetti documentation of https://github.com/bmizerany/pq https://github.com/bmizerany/pq and http://golang.org/pkg/database/sql/ http://golang.org/pkg/database/sql/ and have yet to find anything useful. Hacking around only leaves me with frustration. Searching around only leads me to unhelpful descriptions of what the GO command does in MSSQL Server.
- luriel 14y agoIf you have Go questions and can't find the answer anywhere, feel free to post to StackOverflow or http://reddit.com/r/golang http://reddit.com/r/golang You will get an answer quite fast.
- kkowalczyk 14y agoSeriously? What is "cryptic" about db = sql.Open(...); db.Exec(); db.Query(); db.Close() etc.? From https://github.com/bmizerany/pq/blob/master/conn_test.go https://github.com/bmizerany/pq/blob/master/conn_test.go: func TestExec(t *testing.T) { db := openTestConn(t) defer db.Close() _, err := db.Exec("CREATE TEMP TABLE temp (a int)") if err != nil { t.Fatal(err) } r, err := db.Exec("INSERT INTO temp VALUES (1)") if err != nil { t.Fatal(err) } What is it exactly that you tried and failed? Because looking at conn_test.go the API is dead simple and pretty much like any other db API of this kind.
- richcollins 14y agoYou should look at LuaJIT's coroutines as an option.
- niels_olson 14y agoI keep hearing about go. I am new-ish to programming. Only really getting started on my first project, which depends on pyparsing, which depends on other things. Is go something a novice should be attacking real-world problems with?
- effinjames 14y agoif you're programming for the web, give it a Go.
- slurgfest 14y agoIf you want to get ordinary web jobs it would pay off better to pick up Javascript and Rails or Django.
- TillE 14y agoProbably not. Go is still in its infancy and not especially well-supported. If pyparsing is solving your problems well, stick with it. Python is great. Of course if speed is important, you should ditch CPython anyway.
- jimbokun 14y agoI'm not a Go developer, but if you start having performance problems in Python, that might be a good time to consider Go.
- zemo 14y agoit depends on what your goal is. A language is just one tool in your toolset. I love using Go very much, but I would recommend learning on Python. There's much more literature out there, more people you know will know it, and if you are looking for work, there are effectively zero entry-level programming jobs in Go. The reason you keep hearing about Go is that it just very recently hit its 1.0 release, so it it a comparatively new language, and it's just a topic of conversation. Talking about Python isn't really newsworthy in the same way, because for most people, it's just a fact of everyday life. Go, however, is this new thing that's a little mysterious, that most people haven't tried yet. Have you ever heard the phrase "you can never really know yourself until you know others"? Well, that's true of programming languages, too; learning new languages can help you to write better code in a language you were already familiar with. Learning new languages is great practice for any programmer. But to start, I would just stick with Python and write some things that make you happy. That's the most important part; to figure out how to use code to make yourself happy. Which language you use is really just an implementation detail.
- rWolfcastle 14y agoBased solely on this article, and this article alone, and knowing nothing more about Jordan Orelli, the conclusion I drew about the author is that he thinks "Java Web Programming" strictly refers to applets and nothing more. narrows eyes... /nerdhate
- zemo 14y agoah, I was working on a game. It was a space shooter. I wanted it to be viewable in the browser so that I could just send a link to my friends and they wouldn't have to download it. I wound up writing it in AS3.
- zzzeek 14y agoFor small tasks in the background of a web request, you can just, you know, use a worker thread. This author seemed like he didn't even try regular threads, and went directly from one hyped meme to another. It's often the case that the GIL isn't much of an issue.
- electic 14y agoI am writing an article right now about how I went from Go to C++. Hope I can have something up here soon.
- markmm 14y agoWait a few weeks, for his followup article entitled "Why I came back to Python....two words...mature libraries"
- luriel 14y agoI have found at least the Go stdlib much more uniform and reliable than the Python stdlib, perhaps not as complete, but it also has things that are missing from the Python stdlib, and unlike with the Python stdlib I don't find myself reaching from alternatives, the Go stdlib has great template system, image manipulation, etc. Third party libs still are more uneven, but that is not much different from Python.
- rjurney 14y agoI'm always confused by these kinds of posts. You really only code in one language most of the time? I code in half a dozen every day.
- mathattack 14y agoI think we can cut the author some slack. He admits in the header to being a relative newbie. (Learned python less than a year ago, recently took Hacker School)
- dude8 14y agoI would have to say the author of this article probably isn't that good.
- Kiro 14y agoWhy do you need concurrency to send an e-mail? Why do you need to do it in the background? Just trying to understand this concurrency thing...
- zemo 14y agoit's a slow, i/o operation. Perhaps it takes a second or two; maybe you're generating a PDF and the email is of non-negligible size, or your email provider is slow that day, or something of that nature. When a user clicks a button, and the response is just "ok, we sent you an email", you can just issue that response immediately, and then send the email. Otherwise, the user's browser just sits there and hangs until the entire process is done.
- deleted 14y ago[deleted]
- se85 14y agoI prefer the node.js example given over the Go example. Sure, the go example is shorter, but the JavaScript version reads much nicer (at least to me).
- dgregd 14y agoAccording to Google Dart is better for web apps than Go. Is it too early to switch from Python/Ruby to Go? Maybe it would be better to wait for Dart. I known that Mozilla and MS won't support Dart however they also don't support Go.
- sproketboy 14y agoWhy would anyone go to Node.js? Serious question.
- zemo 14y agononblocking i/o is an important feature if you want to make something responsive that will handle a lot of connections. node.js brings that to the foreground, and makes it a topic that is digestible for a lot of programmers that have never seen it before. The end result is that by using node.js, a developer is able to experience one element of concurrency that they may not have had exposure to using Ruby/Python/PHP. Yes, it is possible to do nonblocking i/o in Ruby/Python/PHP, but it isn't as natural of an experience. It's a usability thing. If you make something easier to use, more people use it; as a result, node.js has introduced the topic of nonblocking i/o in a way that other technologies have been unable to do. It also allows you to share code between the client and the server, which may or may not matter, depending on your project. And for a lot of developers, node.js means using a language you already know. The guys at Kitchen Table Coders do a workshop where they make a physical game paddle with an arduino, and then hook it up to a node.js server, so that two people can play pong with physical controllers in their browser. They're able to teach that workshop in a day. I think that speaks volumes for types of things that you can do with node.js, and its mass appeal. But, like I said in the article, I find that node.js becomes disorganized quickly, and I just plain don't enjoy debugging JavaScript. On top of that, if you need to do CPU-bound work, node.js basically forces you to create an isolated process and perform i/o with it, but now you have an OS process for each of these instances, whereas in Go, creating a goroutine and communicating with it over channels is much more lightweight. Node.js has its merits, and I don't mean to say "nobody use node.js ever!", I just don't like using it.
- deleted 14y ago[deleted]
- deleted 14y ago[deleted]
- ishbits 14y agoWhat's wrong with good old threads. It's not that hard. And threading in Java will blow away any pseudo concurrency setup in python. Been there done that. Love python but just couldn't get the performance we needed.