Buscar..


Introducción

Trabajar con hilos puede necesitar algunas técnicas de sincronización si los hilos interactúan. En este tema, puede encontrar las diferentes estructuras que proporciona la biblioteca estándar para resolver estos problemas.

std :: shared_lock

Un shared_lock puede usarse junto con un bloqueo único para permitir múltiples lectores y escritores exclusivos.

#include <unordered_map>
#include <mutex>
#include <shared_mutex>
#include <thread>
#include <string>
#include <iostream>
    
class PhoneBook {
    public:
        string getPhoneNo( const std::string & name )
        {
            shared_lock<shared_timed_mutex> r(_protect);
            auto it =  _phonebook.find( name );
            if ( it == _phonebook.end() )
                return (*it).second;
            return "";
        }
        void addPhoneNo ( const std::string & name, const std::string & phone )
        {
            unique_lock<shared_timed_mutex> w(_protect);
            _phonebook[name] = phone;
        }
        
        shared_timed_mutex _protect;
        unordered_map<string,string>  _phonebook;
    };

std :: call_once, std :: once_flag

std::call_once garantiza la ejecución de una función exactamente una vez por subprocesos de la competencia. std::system_error en caso de que no pueda completar su tarea.

Se utiliza junto con s td::once_flag .

#include <mutex>
#include <iostream>

std::once_flag flag;
void do_something(){
      std::call_once(flag, [](){std::cout << "Happens once" << std::endl;});
    
      std::cout << "Happens every time" << std::endl;
}

Bloqueo de objetos para un acceso eficiente.

A menudo desea bloquear todo el objeto mientras realiza varias operaciones en él. Por ejemplo, si necesita examinar o modificar el objeto utilizando iteradores . Siempre que necesite llamar a múltiples funciones miembro, generalmente es más eficiente bloquear todo el objeto en lugar de funciones individuales.

Por ejemplo:

class text_buffer
{
    // for readability/maintainability
    using mutex_type = std::shared_timed_mutex;
    using reading_lock = std::shared_lock<mutex_type>;
    using updates_lock = std::unique_lock<mutex_type>;

public:
    // This returns a scoped lock that can be shared by multiple
    // readers at the same time while excluding any writers
    [[nodiscard]]
    reading_lock lock_for_reading() const { return reading_lock(mtx); }

    // This returns a scoped lock that is exclusing to one
    // writer preventing any readers
    [[nodiscard]]
    updates_lock lock_for_updates() { return updates_lock(mtx); }

    char* data() { return buf; }
    char const* data() const { return buf; }

    char* begin() { return buf; }
    char const* begin() const { return buf; }

    char* end() { return buf + sizeof(buf); }
    char const* end() const { return buf + sizeof(buf); }

    std::size_t size() const { return sizeof(buf); }

private:
    char buf[1024];
    mutable mutex_type mtx; // mutable allows const objects to be locked
};

Al calcular una suma de comprobación, el objeto está bloqueado para la lectura, lo que permite que otros subprocesos que desean leer del objeto al mismo tiempo lo hagan.

std::size_t checksum(text_buffer const& buf)
{
    std::size_t sum = 0xA44944A4;

    // lock the object for reading
    auto lock = buf.lock_for_reading();

    for(auto c: buf)
        sum = (sum << 8) | (((unsigned char) ((sum & 0xFF000000) >> 24)) ^ c);

    return sum;
}

Al borrar el objeto, se actualizan sus datos internos, por lo que se debe hacer utilizando un bloqueo exclusivo.

void clear(text_buffer& buf)
{
    auto lock = buf.lock_for_updates(); // exclusive lock
    std::fill(std::begin(buf), std::end(buf), '\0');
}

Al obtener más de un bloqueo, se debe tener cuidado de adquirir siempre los bloqueos en el mismo orden para todos los subprocesos.

void transfer(text_buffer const& input, text_buffer& output)
{
    auto lock1 = input.lock_for_reading();
    auto lock2 = output.lock_for_updates();

    std::copy(std::begin(input), std::end(input), std::begin(output));
}

nota: esto se hace mejor usando std :: deferred :: lock y llamando a std :: lock

std :: condition_variable_any, std :: cv_status

Una generalización de std::condition_variable , std::condition_variable_any funciona con cualquier tipo de estructura BasicLockable.

std::cv_status como estado de retorno para una variable de condición tiene dos códigos de retorno posibles:

  • std :: cv_status :: no_timeout: No hubo tiempo de espera, se notificó la variable de condición
  • std :: cv_status :: no_timeout: variable de condición agotada por el tiempo de espera


Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow