¿Es inteligente reemplazar boost :: thread y boost :: mutex con equivalentes de c ++ 11?

153

Motivación: la razón por la que lo estoy considerando es que mi genio gerente de proyecto piensa que el impulso es otra dependencia y que es horrible porque "usted depende de él" (intenté explicar la calidad del impulso, y luego me di por vencido después de un tiempo :( La razón más pequeña por la que me gustaría hacerlo es que me gustaría aprender las características de c ++ 11, porque la gente comenzará a escribir código en él.

  1. ¿Existe un mapeo 1: 1 entre #include<thread> #include<mutex>y equivalentes de impulso?
  2. ¿Consideraría una buena idea reemplazar las cosas de impulso con cosas de c ++ 11
    ? Mi uso es primitivo, pero ¿hay ejemplos cuando std no ofrece qué impulso? O (blasfemia) viceversa?

PD: uso GCC, así que los encabezados están ahí.

NoSenseEtAl
fuente
46
Las pautas de codificación de Google de la OMI son estúpidas de muchas maneras ... Por ejemplo. no permiten auto desde C ++ 11 ... :)
NoSenseEtAl
55
Pautas de citas: [auto] dificulta la legibilidad [porque elimina] la redundancia comprobada (como los nombres de tipo) que pueden ser útiles para los lectores.
Andrew Tomazos
31
for (auto it = v.begin () ... :)
NoSenseEtAl
15
@ AndrewTomazos-Fathomling: ¿En serio? Personalmente, no creo que me haya importado el tipo real del iterador (bueno, tal vez algunas veces), solo las operaciones compatibles ... Diría que la redundancia sintáctica rara vez es una buena idea (DRY).
Grizzly
16
por cierto google modificado sus directrices mudos por lo que ahora, finalmente, que permiten auto
NoSenseEtAl

Respuestas:

192

Existen varias diferencias entre Boost.Thread y la biblioteca de hilos estándar C ++ 11:

  • Boost admite la cancelación de subprocesos, los subprocesos de C ++ 11 no
  • C ++ 11 es compatible std::async, pero Boost no
  • Boost tiene un boost::shared_mutexbloqueo de lector múltiple / escritor único. El análogo std::shared_timed_mutexestá disponible solo desde C ++ 14 ( N3891 ), mientras que std::shared_mutexestá disponible solo desde C ++ 17 ( N4508 ).
  • Los tiempos de espera de C ++ 11 son diferentes a los tiempos de espera de Boost (aunque esto pronto debería cambiar ahora Boost.Chrono ha sido aceptado).
  • Algunos de los nombres son diferentes (por ejemplo, boost::unique_futurevs std::future)
  • La semántica de paso de argumentos std::threades diferente de boost::thread--- Usos de refuerzo boost::bind, que requiere argumentos copiables. std::threadpermite que los tipos de solo movimiento std::unique_ptrse pasen como argumentos. Debido al uso de boost::bind, la semántica de los marcadores de posición, como _1en las expresiones de enlace anidadas, también puede ser diferente.
  • Si no llama explícitamente join()o detach()el boost::threaddestructor y el operador de asignación llamarán detach()al objeto de hilo que se destruye / asigna. Con un std::threadobjeto C ++ 11 , esto dará como resultado una llamada astd::terminate() y anulará la aplicación.

Para aclarar el punto sobre los parámetros de solo movimiento, lo siguiente es válido C ++ 11, y transfiere la propiedad de intlo temporal std::unique_ptral parámetro de f1cuándo se inicia el nuevo hilo. Sin embargo, si lo usa boost::thread, no funcionará, ya que se usa boost::bindinternamente y std::unique_ptrno se puede copiar. También hay un error en la biblioteca de subprocesos C ++ 11 proporcionada con GCC que impide que esto funcione, ya que también se usa std::binden la implementación allí.

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

Si está utilizando Boost, entonces probablemente pueda cambiar a subprocesos de C ++ 11 sin problemas si su compilador lo admite (por ejemplo, las versiones recientes de GCC en Linux tienen una implementación casi completa de la biblioteca de subprocesos de C ++ 11 disponible en -std=c++0x modo).

Si su compilador no admite subprocesos de C ++ 11, puede obtener una implementación de terceros como Just :: Thread , pero esto sigue siendo una dependencia.

Anthony Williams
fuente
1
Existen métodos separados de bloqueo / desbloqueo para lectores y escritores ( lock/ unlockpara escritores vs. 'lock_shared / unlock_shared' para lectores). Múltiples lectores pueden llamar a lock_shared sin bloquear, siempre y cuando ningún escritor lo esté usando.
Dave S
2
Los shared_mutexdocumentos están en boost.org/doc/libs/1_47_0/doc/html/thread/… . Puede bloquear el mutex como compartido o exclusivo, y luego usar la función de desbloqueo correspondiente. También puede usar los tipos RAII para hacer esto ( shared_locktoma un bloqueo de lectura compartido lock_guardy unique_locktoma un bloqueo exclusivo). He tratado de aclarar el punto sobre los tipos de solo movimiento.
Anthony Williams
3
Una cosa menor que me hizo tropezar: en boost, el destructor de un hilo en ejecución lo separa ( boost.org/doc/libs/1_47_0/doc/html/thread/… ), mientras que en C ++, las llamadas del destructor de un hilo en ejecución terminan () (FDIS 30.3.1.3)
Cubbi
3
En C ++ 11, la try_scoped_lockfuncionalidad está cubierta por std::unique_lock. Hay un constructor que toma un mutex y std::try_to_lock, y luego llamará try_lock()al mutex en lugar de lock(). Ver stdthread.co.uk/doc/headers/mutex/unique_lock/…
Anthony Williams
44
Sí, Boost.Thread se ha acercado mucho más al estándar C ++ 11 desde que escribí esto, principalmente debido al trabajo de Vicente Botet.
Anthony Williams
24

std::threadestá modelado en gran parte después boost::thread, con algunas diferencias :

  • la semántica no copiable de boost, de un controlador de mapas a uno de os, se conserva. Pero este hilo es móvil para permitir el retorno del hilo de las funciones de fábrica y su colocación en contenedores.
  • Esta propuesta agrega cancelación a la boost::thread , lo cual es una complicación significativa. Este cambio tiene un gran impacto no solo en el subproceso sino también en el resto de la biblioteca de subprocesos de C ++. Se cree que este gran cambio es justificable debido al beneficio.
    • El destructor de subprocesos ahora debe llamar a cancelar antes de desconectar para evitar fugas accidentales de subprocesos secundarios cuando se cancelan los subprocesos principales.
    • Ahora se requiere un miembro de separación explícito para habilitar la separación sin cancelar.
  • Los conceptos de identificador de hilo e identidad de hilo se han separado en dos clases (son la misma clase boost::thread). Esto es para facilitar la manipulación y el almacenamiento de la identidad del hilo.
  • Se ha agregado la capacidad de crear una identificación de subproceso que se garantiza que se compara igual que ningún otro subproceso unible ( boost::threadno tiene esto). Esto es útil para el código que quiere saber si está siendo ejecutado por el mismo hilo que una llamada anterior (los mutex recursivos son un ejemplo concreto).
  • Existe una "puerta trasera" para obtener el identificador de subproceso nativo para que los clientes puedan manipular subprocesos utilizando el sistema operativo subyacente si lo desean.

Esto es de 2007, por lo que algunos puntos ya no son válidos: boost::threadtiene una native_handlefunción ahora y, como señalan los comentaristas, std::threadya no tiene cancelación.

No pude encontrar diferencias significativas entre boost::mutexy std::mutex.

Alex B
fuente
2
std::threadno tiene cancelación; es lo boost::threadque hace!
Anthony Williams
@Anthony, ¿estás seguro de que no te refieres interrupt()a boost :: thread? También parece que es una propuesta original, que cambió desde 2007.
Alex B
44
Sí, la cancelación en boost se llama "interrupción". Sí, esta es una vieja propuesta. El último borrador público del estándar C ++ 11 (que incluye la biblioteca de hilos) es open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf
Anthony Williams
6

Hay una razón para no migrar std::thread.

Si está utilizando enlaces estáticos, std::threadqueda inutilizable debido a estos errores / características de gcc:

Es decir, si llama std::thread::detacho std::thread::joinprovocará una excepción o un bloqueo, mientras boost::threadfunciona bien en estos casos.

ks1322
fuente
Veo que un error NO ESTÁ CONFIRMADO y el otro es NO VÁLIDO, con un comentario que dice que el reportero debería haberse vinculado libpthread.a. ¿Estás absolutamente seguro de lo que estás diciendo?
einpoklum
1
@einpoklum, debería poder hacerlo funcionar usando Wl,--whole-archive -lpthread -Wl,--no-whole-archive, vea esta respuesta, por ejemplo stackoverflow.com/a/23504509/72178 . Pero no es una forma muy directa de vincularse libpthread.ay también se considera una mala idea.
ks1322
44
¿Podemos suponer que estos errores están solucionados ya que ahora es 2016? Los errores se publicaron en 2012 y desde gcc 4.9.2, oficialmente admite C ++ 11, por lo que no podemos quejarnos de C ++ 11 antes del soporte oficial.
Splash
6

Caso empresarial

Si está escribiendo software para la empresa que necesita ejecutarse en una variedad moderada a grande de sistemas operativos y, en consecuencia, construir con una variedad de compiladores y versiones de compiladores (especialmente los relativamente antiguos) en esos sistemas operativos, mi sugerencia es mantenerse alejado de C ++ 11 en total por ahora. Eso significa que no puede usar std::thread, y recomendaría usar boost::thread.

Funda Básica / Tech Startup

Si está escribiendo para uno o dos sistemas operativos, sabe con certeza que solo necesitará compilar con un compilador moderno que sea compatible principalmente con C ++ 11 (por ejemplo, VS2015, GCC 5.3, Xcode 7), y aún no lo ha hecho. dependiendo de la biblioteca de impulso, entonces std::threadpodría ser una buena opción.

Mi experiencia

Personalmente, soy partidario de las bibliotecas reforzadas, muy utilizadas, altamente compatibles y altamente consistentes, como boost versus una alternativa muy moderna. Esto es especialmente cierto para temas de programación complicados como el enhebrado. Además, durante mucho tiempo he experimentado un gran éxito con boost::thread(y boost en general) en una amplia gama de entornos, compiladores, modelos de subprocesos, etc. Cuando es mi elección, elijo boost.

Nicholas Smith
fuente
1
@UmNyobe Sin embargo, tiene razón. Muchas implementaciones de subprocesos C ++ 11 están tan rotas que me sorprende que la gente incluso considere usarlo.
StaceyGirl
3

Con Visual Studio 2013, std::mutexparece que se comporta de manera diferente que el boost::mutex, lo que me causó algunos problemas (vea esta pregunta ).

Robert Hegner
fuente
1

Con respecto a std :: shared_mutex agregado en C ++ 17

Las otras respuestas aquí proporcionan una muy buena visión general de las diferencias en general. Sin embargo, hay varios problemas con std::shared_mutexese impulso resuelve.

  1. Muticios actualizables. Estos están ausentes de std::thread. Permiten que un lector se actualice a escritor sin permitir que otros escritores entren antes que usted . Estos le permiten hacer cosas como preprocesar un gran cálculo (por ejemplo, reindexar una estructura de datos) cuando está en modo de lectura, luego actualizar para escribir para aplicar el reindex mientras mantiene presionado el bloqueo de escritura por un corto tiempo.

  2. Justicia. Si tiene actividad de lectura constante con a std::shared_mutex, sus escritores estarán bloqueados indefinidamente. Esto se debe a que si aparece otro lector, siempre se les dará prioridad. Con boost:shared_mutex, todos los hilos eventualmente tendrán prioridad. (1) Ni los lectores ni los escritores estarán hambrientos.

El problema de esto es que si tiene un sistema de alto rendimiento sin tiempo de inactividad y una contención muy alta, std::shared_mutexnunca funcionará para usted sin construir manualmente un sistema prioritario sobre él. boost::shared_mutexfuncionará de la caja, aunque es posible que deba modificarlo en ciertos casos. Yo diría que std::shared_mutexel comportamiento es un error latente que espera suceder en la mayoría de los códigos que lo usan.

(1) El algoritmo real que utiliza se basa en el programador de subprocesos del sistema operativo. En mi experiencia, cuando las lecturas están saturadas, hay pausas más largas (al obtener un bloqueo de escritura) en Windows que en OSX / Linux.

Robert Fraser
fuente
0

Intenté usar shared_ptr desde std en lugar de boost y de hecho encontré un error en la implementación de gcc de esta clase. Mi aplicación se bloqueó debido a que el destructor se llamó dos veces (esta clase debería ser segura para subprocesos y no debería generar tales problemas). Después de mover para impulsar :: shared_ptr todos los problemas desaparecieron. Las implementaciones actuales de C ++ 11 aún no están maduras.

Boost también tiene más características. Por ejemplo, el encabezado en la versión estándar no proporciona el serializador a una secuencia (es decir, cout << duración). Boost tiene muchas bibliotecas que usan sus propios equivalentes, etc., pero no cooperan con las versiones estándar.

En resumen: si ya tiene una aplicación escrita con boost, es más seguro mantener su código tal como está en lugar de esforzarse para pasar al estándar C ++ 11.

usuario3323559
fuente
44
El shared_ptrdestructor no necesita ser seguro para subprocesos, es un comportamiento indefinido que un subproceso acceda a un objeto mientras otro subproceso lo está destruyendo. Si cree que ha encontrado un error en shared_ptr de GCC, infórmelo, de lo contrario, en el balance de probabilidad, lo está usando mal.
Jonathan Wakely