3 ms·
It's hard to pinpoint exactly why I was having so many problems with deadlocks. Most of the time they could easily be solved by having channels with an unbounde
by yorhel 15y ago
It's hard to pinpoint exactly why I was having so many problems with deadlocks. Most of the time they could easily be solved by having channels with an unbounded buffer, thus guaranteeing asynchronous behaviour. But since the Go authors intentionally left that out, I must be doing something wrong at a more fundamental level.
You're probably right about the IRC clients using the latter style of callbacks. But then it turns pretty much into the following style:
irc_connect();
register_event_x(callback_x);
register_event_y(callback_y);
while(read())
dispatch_event();
Which is essentially an event loop with callback-style dispatching. The only major difference here and with regular callback programming is that the callbacks are allowed to block. But in my mind that only complicates things as I can't tell anymore whether I can receive a particular event at some time because I have no idea whether there are any blocking handlers going on. Of course, you can dispatch everything into a separate goroutine, but then suddenly I can't easily argue anymore about the order of message arrival and what effect that has on shared state. Of course, this example may be a bit vague and abstract, but these resemble the kinds of issues I have constantly run in to.
On the scaling on multiple cores: Sure it's a nice advantage when you're already used to concurrent programming, but for most things I've written so far it doesn't really matter. In the few cases that I had a component that required heavy disk I/O or computational resources, it was only a small and easily-identifyable component that could be implemented in a separate OS thread. I'm not familiar with Node, but both glib2 and libev allow spawning a separate event loop in each thread and provide mechanisms to communicate between each other (idle functions in glib, ev_async in libev). Those are for C, however, and I have to admit that callback-style programming isn't too convenient there due to the lack of closures and automatic garbage collection.
- sausagefeet 15y agoI would expect the code to look more like an interface and handing your interface object off to the goroutine in charge of the IRC connection. Or the other way and have a goroutine in charge of shuffling the bytes back and forth and when it parses a package it has a channel to pump the information down and then you receive it and work on it. Either way, this seems much more straight forward than callbacks. > But in my mind that only complicates things as I can't tell anymore whether I can receive a particular event at some time because I have no idea whether there are any blocking handlers going on Don't really get what you mean here. The select operator seems like it would solve the problem your implying. > then suddenly I can't easily argue anymore about the order of message arrival and what effect that has on shared state You can barely, sometimes not even, reason about the order of message arrival and it's effect on state in callbacks, though. At every point in a callback you have the entire state of the program to deal with, at least in Erlang or Go you only have the context of your local process/goroutine to deal with. I have seen plenty of callback code explode because, what seemed like, an obvious sequence of events got reorderd or sent before the previous work was expected to be done, or any variation, and the code didn't handle it properly. You might be thinking "well, duh, that is easy to fix and a silly mistake to make", but it isn't. In a shared memory, especially where mutability is the default mode of action, an event-driven framework can be represented as a function that takes a 2 dimensional matrix: 1 dimension is every variable in my program, the other is every event that can happen in my program. At every point in the program any variation of this matrix must be a valid state. In a language like Go, or Erlang, I can cut this matrix down significantly so that I only have to worry about the matrix involving some subset of events and some subset of variables. In short, that's a massive win IMO.