6 ms·
C++ is still a fantastic language. It's hard to learn to use it effectively but once you do you have one of the most powerful programming tools available under
by jcoffland 11y ago
C++ is still a fantastic language. It's hard to learn to use it effectively but once you do you have one of the most powerful programming tools available under your belt. The two main points to using C++ effectively are 1. you must protect allocated resources with smart pointers and 2. threads should only be used to exercise multiple CPU cores and never as a way to work around blocking IO.
That later point is a little more complicated/subtle and took me years of threaded programming to learn and accept. When adhering to these two rules, C++ is safe and extremely powerful.
- allcentury 11y agoAny reading you'd recommend regarding your second point? I've never heard that about C++ before.
- stefs 11y agoi think this is strange. i'm not a c++ programmer, but the evented frameworks (node.js or vert.x) are doing it the other way round: use threads only for IO (or computation so heavy it's practically blocking). the complete opposite.
- misframer 11y agolibuv (used by Node.js) uses epoll, kqueue, etc. for network I/O. It uses a thread pool for all file operations.
- stefs 11y agoah, well - when reading "blocking io" i first thought about the file system. so does my point still stand? using a threadpool for disk IO does go against "threads should only be used to exercise multiple CPU cores and never as a way to work around blocking IO." or am i still not getting this?
- jcoffland 11y agoActually node.js does it the same way. Under the covers node.js handles IO with libev which is an event library.
- misframer 11y agoThey use libuv now.
- deleted 11y ago[deleted]
- jcoffland 11y agoThere are really two parts to point 2. 2.a. is about performance. And while not specifically about C++ this article covers the main idea http://www.kegel.com/c10k.html http://www.kegel.com/c10k.html 2.b. is about avoiding deadlocks and race conditions. Threaded programming in C++ is dangerous. For years I would have said, that I could avoid deadlocks and race conditions by virtue of being a good programmer. However, I've debugged enough of my own code to know this is not always true. If you only use threads for data driven computation, then you greatly reduce the risks. On the other hand if you have multiple threads running around accessing anything and everything in your program then you need a lot of thread locks which is tedious to implement and not well supported in C++ (vs. say the synchronized keyword in Java), does not perform well and still it is very easy to make mistakes. The point is that there is no reason to let threads run willy nilly because you can do event driven programming in C++ (libev, libevent, libuv) and most programs (or rather most parts of a program) are not computationally intensive. edit: Here's a great resource on the topic of threading. From the SQlite FAQ "Threads are evil. Avoid them." http://www.eecs.berkeley.edu/Pubs/TechRpts/2006/EECS-2006-1.pdf http://www.eecs.berkeley.edu/Pubs/TechRpts/2006/EECS-2006-1....
- deleted 11y ago[deleted]
- brobinson 11y agoThis paper by the guy who runs Mailinator is really interesting: https://www.mailinator.com/tymaPaulMultithreaded.pdf https://www.mailinator.com/tymaPaulMultithreaded.pdf It's about Java, not C++, but it's still relevant.
- exprI 11y agoI mostly agree. However, C++ really isn't that hard to learn. Harder to learn than some alternatives, perhaps, but only like driving a stick shift is harder than an AT, but neither really qualifies as simply hard.
- jcoffland 11y agoI bet I could name a few things about C++ you probably don't know. One small example, with out looking it up can tell me how to add a prefix operator (e.g. ++) to a class vs. a postfix operator? The answer is ugly. There are a lot details to C++ (and I'm only talking about C++98 at this point). It's easy to learn enough C++ to create some programs but it's hard to really learn C++.
- SamReidHughes 11y agoYes. And even if I didn't, it would be easy enough to look up, and there's no danger to me for not knowing it. That's not where C++'s learning curve has its sharp edges.
- ape4 11y agoThe obvious reason to use threads... so you can do multiple things simultaneously.
- jcoffland 11y agoThis statement misses the point. What I am saying is only use threads to compute things simultaneously and not to perform IO operations simultaneously.
- ape4 11y agoI understand what you saying but its too strict. Say the things you want to compute simultaneously involve IO. For example your application has a window that displays a log by watching for writes to the end of a log file (like tail). This is easy and reasonable to do in a thread.
- thrownaway2424 11y ago#2 seems questionable. I always program in a thread-per-request style and start threads for blocking sub-operations of top level requests as well. I know there are platforms where this doesn't work well and I also know a lot of people cut their teeth on dreadful systems like sunos where it took ten seconds to start a thread, but on modern Linux threads are dirt cheap and well worth the clarity that synchronous programming allows.
- dclusin 11y agoA context switch takes about 50 microseconds. If you want to handle 10,000 requests in a second 500 milliseconds is lost to context switching alone. Probably more. The nature of IO & user input in general is to hurry up and wait. It's usually simpler to do async requests & polling file descriptors rather than thread-per-request. The former seems on the surface more complicated and more lines of code, but IMO, ends up being less complicated because you won't have to spend time digging through logs trying to figure out that the reason some other unrelated thread stalled for so long is because you were blasted with thousands of requests in the same second.
- thrownaway2424 11y agoWhere did you get that number? That's the kind of SunOS derangement I'm talking about. On Linux with NPTL (which is over a decade old) and any recent Intel CPU (Westmere or later) a context switch between threads of the same process takes only about 1-2 microseconds.
- jcoffland 11y agoRegardless, context switching, waiting on locks and the added cost of allocating a stack to each thread are all relatively expensive. I've gotten huge performance increases by rewriting threaded code to instead use non-blocking IO. Nginx vs. Apache is a great example of this difference. https://www.nginx.com/blog/inside-nginx-how-we-designed-for-performance-scale/ https://www.nginx.com/blog/inside-nginx-how-we-designed-for-...
- amelius 11y agoThis works, until you start coding an imperative-style interpreter in the functional language, and mess everything up again :) Anyway, I believe we should have the best of both worlds: a language which for the outer layers (the "outer loops" so to speak) uses functional style, and for the inner parts allows imperative style (of course the programmer should make sure that no side-effects remain visible here to the encompassing layer).
- chriswarbo 11y agoTake a look at Haskell's Single Threaded (ST) data type, which does exactly this: https://wiki.haskell.org/Monad/ST https://wiki.haskell.org/Monad/ST
- vamega 11y agoST is the efficient version of the State Monad. Where did you come to think that St was short for single threaded?
- chriswarbo 11y agoFrom https://wiki.haskell.org/Monad/ST https://wiki.haskell.org/Monad/ST > The ST monad lets you use update-in-place, but is escapable (unlike IO). ST actions have the form: ST s α > Meaning that they return a value of type α, and execute in "thread" s. All reference types are tagged with the thread, so that actions can only affect references in their own "thread". > Now, the type of the function used to escape ST is: runST :: forall α. (forall s. ST s α) -> α > The action you pass must be universal in s, so inside your action you don't know what thread, thus you cannot access any other threads, thus runST is pure. This is very useful, since it allows you to implement externally pure things like in-place quicksort, and present them as pure functions ∀ e. Ord e ⇒ Array e → Array e; without using any unsafe functions.
- groovy2shoes 11y agoTake a look at Scheme, Racket, OCaml, and Standard ML.
- amelius 11y ago> threads should only be used to exercise multiple CPU cores and never as a way to work around blocking IO. Well, nothing, except the simplest of tasks, is pure IO. If you are talking about asynchronous programming, that is basically the cooperative multitasking of the Windows 3.1 era. One event handler will, if it performs a lot of computations, block another from running. Most people seem to forget that the CPU is a resource that can be blocked too. So in the end you still end up spawning threads to reduce latency.
- jcoffland 11y agoYou may be right that a lot of software has some CPU bounds parts but I would argue that most code is IO bound. Usually there is a only a small kernel in a program that actually does heavy computation and this is the only place you should use threading.
- Coding_Cat 11y agoWhat's wrong with threading on IO? For example if you have a webserver which has to wait on replies from various other resources while serving enough clients to saturate itself. Sure, performance wise it is probably better to have a fixed number of threads which do their own scheduling in userspace but this requires much more work then a simple if(!finished()){yield();} and making a thread per client.
- jacques_chester 11y ago> What's wrong with threading on IO? To modify JWZ's famous joke: You have a problem. You say, "I know, I'll use threads!" Nowyou prob h2ave lems
- mystikal 11y agohaha, also: Now y
- pjkundert 11y agoGreat advice, in general. One small point to remember is that it is impossible to implement a completely non-blocking output using the C++ ostream classes. The underlying streambuf doesn't provide intrinsics to measure the remaining capacity of the output buffer, and may be forced to flush at any output request -- causing unavoidable blocking. Of course, it is possible to implement your own streambuf class which provides an interface to measure remaining output buffering capacity. Just something to know... C++ remains my favourite language; after a couple decades of intensive development using it, pretty much every other language always leaves me feeling "crippled", somehow.
- jcoffland 11y agoYou are right and I think this is a major flaw with the standard library.
- greydius 11y ago> threads should only be used to exercise multiple CPU cores and never as a way to work around blocking IO Never use threads in C++. Gotcha. Good thing I'm learning Rust, then.