Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

In Java/C# land, the typical solution would be to have a complex conditional expression, all the threads spinning on a .wait() there and then over-use of .notifyAll() instead of .notify() causing lots of spurious wakeups and wasting precious cpu cycles.

The typical solution is to use the higher-level constructs provided by the standard library that take care of these details for you. In Java, at least, explicit mutexes, notify, etc in application code are roughly the equivalent of typing 'AES'. I'm somewhat surprised this C# article happily tells you to just keep adding locks until you think things work. "I mean if you have a hunchback, just throw a little glitter on it, honey, and go dancing."



It's more complicated than that. Implicit synchronization and multithreading in libraries is error-prone, poorly performant, has a significant cognitive overhead. It's a liability if you are chasing performance and a liability if you aren't. So to be good citizens libraries have to assume that a user might need a complete control in choosing and using a concurrency model (of course unless they are the ones providing concurrency models themselves).


I'm talking about libraries that provide higher-level concurrency constructs not implicit multithreading in a library that does, say, image manipulation. The whole point of the former is to reduce cognitive load and errors.


I assumed you were talking about things like concurrent queues, etc. They do implicit synchronization and enforce a specific concurrency model, so they should not be used by libraries.


Yes, but I think he also meant things like the TPL, PLINQ, TPL Dataflow, RX, Akka.NET and so on. I have been happily using one or more of these to write tons of event driven and/or parallel client-side code and haven't touched a lock in years.

And libraries exposing APIs through observables is a great idea.


I don't dislike TPL and PLINQ (though I don't much care for Akka.NET at all), but at the same time I pretty regularly write code with locks. There are problems for which the simplest solution is to treat it stupidly and imperatively and to run those threads side-by-side with well-defined critical sections.


For dead-simple parallel execution of a handful of jobs, IMO nothing beats mapping data to tasks with Linq and then "await Task.WhenAll". And if they have to share data, use something in System.Collections.Concurrent.


For fun bits of API inconsistency there, would you think that...

"task.Wait()" is to "await task" as "Task.WaitAll(tasks)" is to "await Task.WhenAll(tasks)"?

Maybe it's just me, but I sure did.


WhenAll returns a task (a promise, essentially) that completes "when all" its arguments have completed.


I seem to have misremembered, the odd one out was WhenAny, and the issue is that it crashes when given the empty set, instead of waiting forever (which is fine if you're dealing with a specific number of tasks, but not fine if you're actually using it for a dynamic number of tasks).

I could have sworn it was WaitAll, but heck - clearly not! Sorry :-).


Whether they should or should not be used by libraries is a completely different kettle of turtle wax, I did say 'application code' above.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: