Is it smart to replace boost::thread and boost::mutex with c++11 equivalents?

44,538

Solution 1

There are several differences between Boost.Thread and the C++11 standard thread library:

  • Boost supports thread cancellation, C++11 threads do not
  • C++11 supports std::async, but Boost does not
  • Boost has a boost::shared_mutex for multiple-reader/single-writer locking. The analogous std::shared_timed_mutex is available only since C++14 (N3891), while std::shared_mutex is available only since C++17 (N4508).
  • C++11 timeouts are different to Boost timeouts (though this should soon change now Boost.Chrono has been accepted).
  • Some of the names are different (e.g. boost::unique_future vs std::future)
  • The argument-passing semantics of std::thread are different to boost::thread --- Boost uses boost::bind, which requires copyable arguments. std::thread allows move-only types such as std::unique_ptr to be passed as arguments. Due to the use of boost::bind, the semantics of placeholders such as _1 in nested bind expressions can be different too.
  • If you don't explicitly call join() or detach() then the boost::thread destructor and assignment operator will call detach() on the thread object being destroyed/assigned to. With a C++11 std::thread object, this will result in a call to std::terminate() and abort the application.

To clarify the point about move-only parameters, the following is valid C++11, and transfers the ownership of the int from the temporary std::unique_ptr to the parameter of f1 when the new thread is started. However, if you use boost::thread then it won't work, as it uses boost::bind internally, and std::unique_ptr cannot be copied. There is also a bug in the C++11 thread library provided with GCC that prevents this working, as it uses std::bind in the implementation there too.

void f1(std::unique_ptr<int>);
std::thread t1(f1,std::unique_ptr<int>(new int(42)));

If you are using Boost then you can probably switch to C++11 threads relatively painlessly if your compiler supports it (e.g. recent versions of GCC on linux have a mostly-complete implementation of the C++11 thread library available in -std=c++0x mode).

If your compiler doesn't support C++11 threads then you may be able to get a third-party implementation such as Just::Thread, but this is still a dependency.

Solution 2

std::thread is largely modelled after boost::thread, with a few differences:

  • boost's non-copyable, one-handle-maps-to-one-os-thread, semantics are retained. But this thread is movable to allow returning thread from factory functions and placing into containers.
  • This proposal adds cancellation to the boost::thread, which is a significant complication. This change has a large impact not only on thread but the rest of the C++ threading library as well. It is believed this large change is justifiable because of the benefit.
    • The thread destructor must now call cancel prior to detaching to avoid accidently leaking child threads when parent threads are canceled.
    • An explicit detach member is now required to enable detaching without canceling.
  • The concepts of thread handle and thread identity have been separated into two classes (they are the same class in boost::thread). This is to support easier manipulation and storage of thread identity.
  • The ability to create a thread id which is guaranteed to compare equal to no other joinable thread has been added (boost::thread does not have this). This is handy for code which wants to know if it is being executed by the same thread as a previous call (recursive mutexes are a concrete example).
  • There exists a "back door" to get the native thread handle so that clients can manipulate threads using the underlying OS if desired.

This is from 2007, so some points are no longer valid: boost::thread has a native_handle function now, and, as commenters point out, std::thread doesn't have cancellation anymore.

I could not find any significant differences between boost::mutex and std::mutex.

Solution 3

Enterprise Case

If you are writing software for the enterprise that needs to run on a moderate to large variety of operating systems and consequently build with a variety of compilers and compiler versions (especially relatively old ones) on those operating systems, my suggestion is to stay away from C++11 altogether for now. That means that you cannot use std::thread, and I would recommend using boost::thread.

Basic / Tech Startup Case

If you are writing for one or two operating systems, you know for sure that you will only ever need to build with a modern compiler that mostly supports C++11 (e.g. VS2015, GCC 5.3, Xcode 7), and you are not already dependent on the boost library, then std::thread could be a good option.

My Experience

I am personally partial to hardened, heavily used, highly compatible, highly consistent libraries such as boost versus a very modern alternative. This is especially true for complicated programming subjects such as threading. Also, I have long experienced great success with boost::thread (and boost in general) across a vast array of environments, compilers, threading models, etc. When its my choice, I choose boost.

Solution 4

There is one reason not to migrate to std::thread.

If you are using static linking, std::thread becomes unusable due to these gcc bugs/features:

Namely, if you call std::thread::detach or std::thread::join it will lead to either exception or crash, while boost::thread works ok in these cases.

Solution 5

With Visual Studio 2013 the std::mutex seems to behave differently than the boost::mutex, which caused me some problems (see this question).

Share:
44,538

Related videos on Youtube

NoSenseEtAl
Author by

NoSenseEtAl

Loving C++ because it gives me a warm FUBU feeling, also CHF :P aka: money for programming and high level abstractions for (almost )free. :P I dont hate C that much, but I fu*king hate C APIs

Updated on July 08, 2022

Comments

  • NoSenseEtAl
    NoSenseEtAl almost 2 years

    Motivation: reason why I'm considering it is that my genius project manager thinks that boost is another dependency and that it is horrible because "you depend on it"(I tried explaining the quality of boost, then gave up after some time :( ). Smaller reason why I would like to do it is that I would like to learn c++11 features, because people will start writing code in it. So:

    1. Is there a 1:1 mapping between #include<thread> #include<mutex>and boost equivalents?
    2. Would you consider a good idea to replace boost stuff with c++11
      stuff. My usage is primitive, but are there examples when std doesnt offer what boost does? Or (blasphemy) vice versa?

    P.S. I use GCC so headers are there.

    • Andrew Tomazos
      Andrew Tomazos about 12 years
      Your project manager is not alone in partially or completely banning boost. From Google coding guidelines: "Some Boost libraries encourage coding practices which can hamper readability, such as metaprogramming and other advanced template techniques, and an excessively "functional" style of programming."
    • NoSenseEtAl
      NoSenseEtAl about 12 years
      IMO Google coding guidelines are stupid in many ways... For eg. they dont allow auto from C++11... :)
    • Andrew Tomazos
      Andrew Tomazos about 12 years
      Quoting guidelines: [auto] hampers readability [because it removes] checked redundancy (such as type names) that may be helpful to readers.
    • NoSenseEtAl
      NoSenseEtAl about 12 years
      for ( auto it=v.begin()... :)
    • Andrew Tomazos
      Andrew Tomazos about 12 years
      ? Yes, so the point is that the reader now needs to know the type of "v" to figure out what the type of "it" is.
    • Grizzly
      Grizzly over 11 years
      @AndrewTomazos-Fathomling: Really? Personally I don't think I've ever cared about the actual type of the iterator (well maybe a few times), only the supported operations... I would argue that syntactical redundancy is rarely a good think (DRY).
    • Andrew Tomazos
      Andrew Tomazos over 11 years
      @Grizzly: You don't care about the type of the iterator if you know what the type of v is, as the writer does, however the reader may not have v's declaration handy (maybe it is member variable for example), but if iterator was explicitly declared he could see it. If you Don't Repeat Yourself you only have to say something wrong once to create a bug, and people listening to you better be listening careful for you shall say things only once. (Why do airline attendants "crosscheck"?) The time taken to type in code is NOT the bottleneck in software development.
    • Grizzly
      Grizzly over 11 years
      @AndrewTomazos-Fathomling: Not hust when I have the containers declaration handy. In general when iterating over a container I do not care at all about the type of the container. Often enough I don't even care about the contained type, only about the operations possible on that (which is often clear from the code using it). Mentioning the complete type of long type expressions (iterators, smartpointer) only makes the code less compact and therefore less readable (I don't want to read all that) and harder to change while adding little to no value. So I stand by my statement.
    • Andrew Tomazos
      Andrew Tomazos over 11 years
      @Grizzly: I've noted that people that spend a lot of time working on small projects that they write themselves generally have that attitude, if you work on large codebases with teams of programmers you spend a lot of time reading/debugging code that you didn't write, so your tastes change.
    • Grizzly
      Grizzly over 11 years
      @AndrewTomazos-Fathomling: You are missing my point. I'm saying that I often don't care about the type when reading the code (mine or that of other developers). Not because I've written the code and know the type (chances are I didn't/don't), but simply because often enough it simply doesn't matter to the code. In those cases explicitely mentioning the type leads to more convoluted, which makes it harder to find the actual bugs in the code. This doesn't mean that types where it matters shouldn't be mentioned.
    • Andrew Tomazos
      Andrew Tomazos over 11 years
      @Grizzly: The reason that knowing the type of variables is helpful is that without them you don't know which function is dispatched on an overload, operator or method call, so you cannot step in statically - either in your imagination or actually in your ide. Many times this is key to understanding what is going on (or going wrong). Further the name of the type gives clues about its intended semantics, and also lets you know where the variable memory layout is defined.
    • Grizzly
      Grizzly over 11 years
      @AndrewTomazos-Fathomling: I don't disagree that this can be helpful in some situations. However that isn't universely applicable: I don't think I want ever to step into a function in the standard library (well sometimes, but I usally regret it). So I usually don't care about the type of an iterator. I might care about the underlying type in some cases, so I might e.g. create a reference to the element pointed to by the iterator (compiler not supporting ranged for unfortunately), if that knowledge is necessary for understanding the code.
    • Andrew Tomazos
      Andrew Tomazos over 11 years
      @Grizzly: So the point is for (auto it=v.begin()...) it->foo(); - which function foo is called.
    • NoSenseEtAl
      NoSenseEtAl almost 11 years
      btw google modified its dumb guidelines so now finally they allow auto
    • JAB
      JAB over 8 years
      @AndrewTomazos I feel auto is fine in cases like auto foo = dynamic_cast<Foo*>(bar); and other such situations where the result type is obvious from the supplied expression.
    • HelloGoodbye
      HelloGoodbye over 8 years
      I don't think it's blasphemy to imply that Boost doesn't offer what std (the standard library) does. Boost is supposed to be complementary to std, and as such doesn't contain everything std does.
    • HelloGoodbye
      HelloGoodbye over 8 years
      From Boost's web page: "Ten Boost libraries are included in the C++ Standards Committee's Library Technical Report (TR1) and in the new C++11 Standard. C++11 also includes several more Boost libraries in addition to those from TR1. More Boost libraries are proposed for standardization in C++17." So yes, there are stuff std doesn't offer that Boost offers. :)
  • Anthony Williams
    Anthony Williams over 12 years
    std::thread doesn't have cancellation; it is boost::thread that does!
  • Alex B
    Alex B over 12 years
    @Anthony are you sure you don't mean interrupt() for boost::thread? Also it appears that it's an original proposal, which changed since 2007.
  • Anthony Williams
    Anthony Williams over 12 years
    Yes, cancellation in boost is called "interruption". Yes, this is an old proposal. The latest public draft of the C++11 standard (which includes the thread library) is open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf
  • NoSenseEtAl
    NoSenseEtAl over 12 years
    Can you expand on the shared_mutex? I mean how can the mutex know if it is being locked by a reader or writer? I might have misunderstanding of what it does so my question is meaningless. Regarding Just::Thread I use it as documentation resource because it has nice list of the headers, but I use GCC so I have no need for temporary library(for eg. until MSVC doesnt implement all c++11 headers). Also can you please provide example for your last point, I'm a bit confused about it.
  • Dave S
    Dave S over 12 years
    There are separate lock/unlock methods for readers and writers (lock/unlock for writers vs. 'lock_shared/unlock_shared' for readers). Multiple readers can call lock_shared without blocking, as long as no writers are using it.
  • Anthony Williams
    Anthony Williams over 12 years
    The shared_mutex docs are at boost.org/doc/libs/1_47_0/doc/html/thread/…. You either lock the mutex as shared or exclusive, and then use the corresponding unlock function. You can also use the RAII types to do this (shared_lock takes a shared read lock, and lock_guard and unique_lock take an exclusive lock). I've tried to clarify the point about move-only types.
  • Cubbi
    Cubbi over 12 years
    One more minor thing that tripped me up: in boost, destructor of a running thread detaches it ( boost.org/doc/libs/1_47_0/doc/html/thread/… ), while in C++, destructor of a running thread calls terminate() (FDIS 30.3.1.3)
  • Anthony Williams
    Anthony Williams over 12 years
    In C++11, try_scoped_lock functionality is covered by std::unique_lock. There is a constructor that takes a mutex and std::try_to_lock, and will then call try_lock() on the mutex rather than lock(). See stdthread.co.uk/doc/headers/mutex/unique_lock/…
  • Ghita
    Ghita over 11 years
    Actually thee seems to be boost async now.
  • Anthony Williams
    Anthony Williams over 11 years
    Yes, Boost.Thread has become much closer to the C++11 standard since I wrote this, primarily due to the work of Vicente Botet.
  • einpoklum
    einpoklum about 9 years
    I see that one bug is UNCONFIRMED and the other is INVALID, with a comment saying the reporter should have linked against libpthread.a. Are you absolutely sure in what you're saying?
  • ks1322
    ks1322 about 9 years
    @einpoklum, you should be able to make it work by using Wl,--whole-archive -lpthread -Wl,--no-whole-archive, see this answer for example stackoverflow.com/a/23504509/72178. But it is not very straightforward way to link with libpthread.a and also considered bad idea.
  • Jonathan Wakely
    Jonathan Wakely almost 9 years
    The shared_ptr destructor doesn't need to be thread-safe, it's undefined behaviour to have one thread accessing an object while another thread is destroying it. If you think you've found a bug in GCC's shared_ptr please report it, otherwise on the balance of probability you're using it wrong.
  • einpoklum
    einpoklum almost 9 years
    Is the bug of using std::bind for std::thread arguments still there? Can someone link to a bug page somewhere?
  • Splash
    Splash over 8 years
    Can we assume these bugs are fixed as this is now 2016? The bugs were posted in 2012 and from gcc 4.9.2, it officially supports C++11 so we can't complain C++11 before the official support.
  • Abhinav Gauniyal
    Abhinav Gauniyal over 7 years
    standard should've included thread cancellation by now since boost already supports it.
  • StaceyGirl
    StaceyGirl about 6 years
    @UmNyobe He is right though. Many implementations of C++11 threading are so broken I am surprised people even consider using it.