Question

I am fairly experienced with async and concurrent programming in c# using event, actor, and task based patterns, but now I have a task in c++ with which I have basic familiarity and it needs some asynchronicity and concurrency. I have free reign on deciding how to do this, and do not have the required c++ experience to reinvent the wheel. So after consulting the Google, I see several libraries and frameworks to add this in, but little independent discussion on them, so users of Stack Exchange, what experiences have you had with async libraries in c++? I'm not asking which is best or even which I should use, but more have you used one or more, pros and cons of it as you have experienced, etc, pretty much what is your informal review?

Every library in the world uses the same descriptors, so the official Web sites have been consulted but only help so much, and all seem to offer the same basic features that I require (light cross domain concurrency, efficient thread yields/continuation, message based eventing, thread safe method availability). So all things being more or less equal, I'm playing the dangerous game of fishing for the developer opinion. So thoughts? Thanks much.

While I feel that this question hits at minimum 4 of the subjective guidelines (not mindless fun, asking for experiences over opinions, looking for longer answers which if not purely personal experience should be backed by reference). I was wrong to ask this question here, when posting this I was thinking the problem with subjective requests is that more often than not they are being treated as objective answers, which was not my aim. The true problem is that it is impossible to ask for general opinions and have one true answer, this is not the venue to start a discussion about personal impressions on the tools used by the target demographic. To be clear all of the libraries I find claim to solve the same problems so rather than being problem specific I was looking to compare subjective opinions. I know there is no objective 'best' one my hope was for something akin to: lib a provided more options than b, but I prefer b's naming conventions. This was my mistake. The answer given and marked as such may be helpful for people, great general points to keep in mind about concurrency. Thank you all for your participation.

Était-ce utile?

La solution

(Disclaimer: I only have experience in parallelizing computational workloads. I do not have experience in async I/O. The opinions herein are merely based on reading online articles, blogs and Q&A websites.)

Applying concurrency to a C++ project is more or less a rigorous exercise in software engineering risk management. You have to choose strategies based on your acceptable level of risk; you have to keep your options open so that if the current strategy comes to a dead end you can switch to another strategy without losing a significant amount of completed work; you have to research and prototype a bit further than the current implementation schedule to keep yourself well-informed; avoid the latest and not-yet-proven fads, etc.

A few risks specific to concurrency:

  • Any third-party dependency not specifically designed to be usable in multi-threaded environment will be an additional risk.
  • Any closed-source dependency whose vendor has not fully-committed to supporting its users in multi-threaded environment will be an additional risk, and usually has no mitigations other than switching to an alternative.
    • Firstly, if it is not engineered to a high standard of thread-safety, it typically cannot be trusted in a multi-threaded environment.
    • "Fully-committed to supporting" means that they are willing to take the extra effort of investigating sporadic race conditions on your behalf as long as you can provide a proof-of-concept that (sporadically) reproduces the race condition. This assumes you are using the library correctly in the first place.
  • Computational workloads are relatively a lot easier to parallelize than I/O (latency) workloads.
  • If your workload is network-based and/or disk-based, you can lower the risk by using the existing asynchronous I/O that is provided either natively by the OS, or libraries such as boost::asio.
    • If this is your case, tackling async I/O would be your first prototypical step toward success.
    • This means you have to tolerate the proliferation of callbacks. You trade tediousness (lack of expressiveness, more code maintenance overhead, more documentations) for lowered risk (of using a tried-and-true approach).
  • Library-based C++ Coroutine is a somewhat high-risk approach.
    • You are basically playing sleight-of-hand before the operating systems.
    • C++ Coroutine implementation is tied to specific OS versions (say, at least on Windows). New OS versions may implement new process-level features which breaks pre-compiled C++ code linked against an outdated version of Coroutine.
  • If you use threading library, consider carefully whether the library has the full blessing of the OS vendor.
    • This typically means Intel TBB for Linux-based x86 systems, Microsoft PPL for x86-based Windows (Desktop and Server), etc.
  • Do not put too much hope on "silver-bullets" which claims to minimize necessary changes to legacy code. Typically, applying concurrency imply making massive changes to legacy code.
  • If your application is also memory-bound, try to see if you can mitigate that just by buying more and more RAM (and for your bill-payers to do the same). Otherwise, memory-bound issues can be hard to solve on top of concurrency.
Licencié sous: CC-BY-SA avec attribution
scroll top