AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 77789962
Accepted
SpeakX
SpeakX
Asked: 2024-01-10 06:02:40 +0800 CST2024-01-10 06:02:40 +0800 CST 2024-01-10 06:02:40 +0800 CST

Ponteiro compartilhado com contagem máxima de uso predefinida

  • 772

Contexto : Eu tenho uma fila que suporta leitura única/gravação única de dois threads diferentes (/ou não), para impor esse comportamento, ou seja, leitor único/gravador único por vez, preciso limitar o número de threads proprietários a fila por vez para 2 (o escritor já possui a fila), eu estava pensando em criar um shared_ptr para a fila com uma contagem máxima de referências conhecida em tempo de compilação definida como 2. Portanto, minha pergunta é a seguinte.

Pergunta : Existe uma maneira de implementar um ponteiro compartilhado (talvez usando unique_pointer's) com uma contagem máxima de referências conhecida em tempo de compilação? Meu caso é, max_ref_count = 2ou seja, exceder o limite de contagem de referências = 2 deve ser um erro em tempo de compilação.

const auto p    = std::make_shared<2, double>(2159); //works fine
const auto q    = p; // works fine
const auto err1 = p; // does not compile
const auto err2 = q; // does not compile
c++
  • 2 2 respostas
  • 112 Views

2 respostas

  • Voted
  1. Best Answer
    Jan Schultke
    2024-01-10T06:59:45+08:002024-01-10T06:59:45+08:00

    Impossível em tempo de compilação

    O que você está fazendo não é possível em tempo de compilação, apenas em tempo de execução. Observe que std::shared_ptré copiável, portanto é possível que "os caminhos de cópia diverjam":

    std::shared_ptr A = /* ... */;
    auto B = A;
    auto C = A;
    

    Neste ponto, Bnão Csabemos nada um sobre o outro e não há como garantir que o façam através do sistema de tipos. Na melhor das hipóteses, você pode contar o comprimento do caminho da cópia até Aas Bcópias subsequentes, mas não a quantidade global de cópias.

    Isso exigiria alguma forma de metaprogramação com estado e C++ não suporta isso.

    Difícil em tempo de execução

    Observe dois problemas:

    1. std::shared_ptrtambém se espera que tenha contagem segura de threads. Isto também significa que std::shared_ptr::use_count()pode não produzir o resultado mais recente e não pode ser considerado uma métrica confiável.
    2. Se você mesmo acompanhasse a contagem de uso, precisaria alocar um novo intalém do contador atômico que os ponteiros compartilhados já possuem. Isso é um pouco chato, mas factível.

    Solução de thread único

    Se você não considerar os problemas do multithreading, poderá apenas usar std::shared_ptr::use_count()para controlar os usos. Esta é uma métrica confiável em um programa de thread único. Você só precisa fazer um wrapper para std::shared_ptro qual será lançado sempre que o limite for excedido, o que pode acontecer no construtor de cópia e no operador de atribuição de cópia.

    template <typename T, std::size_t N>
      requires (N != 0)
    struct limited_shared_ptr {
        using base_type = std::shared_ptr<T, N>;
        
      private:
        base_type base;
    
      public:
        // OK
        limited_shared_ptr(T* ptr) : base{ptr} {}
    
        // The following contains an immediately invoked lambda expression in the copy
        // constructor.
        // This is clunky, but necessary so that we throw *before* we copy-construct
        limited_shared_ptr(const limited_shared_ptr& other)
          : base{[this] -> base_type& {
            if (other.use_count() >= N) {
                throw /* ... */;
            }
            return other;
        }()} {}
    
        limited_shared_ptr& operator=(const limited_shared_ptr& other) {
            if (this != &other &&
                base.get() != other.get() &&
                other.get() != nullptr &&
                other.use_count() >= N) {
                throw /* ... */;
            }
            base = other.base;
            return *this;
        }
    
        // Move assignment or destruction cannot increase the use_count(), only
        // keep it constant or decrease it, so we can keep these operations defaulted.
        limited_shared_ptr(limited_shared_ptr&& other) = default;
        limited_shared_ptr& operator=(limited_shared_ptr&&) = default;
        ~limited_shared_ptr() = default;
    
        // TODO: expose more of the std::shared_ptr interface
    };
    

    Solução multithread

    Isso é um pouco mais complicado e fornecerei apenas o esboço geral.

    Você pode criar um std::shared_ptrcom um deleter personalizado. Este eliminador personalizado pode conter um std::atomic<std::size_t>para manter um controle confiável da contagem de uso.

    struct counting_deleter {
        std::atomic<std::size_t> use_count;
        void operator()(auto* ptr) const {
            delete ptr;
        }
    };
    

    Com isso, você não precisa gerenciar recursos sozinho, mas pode deixar std::shared_ptrisso e acessar o deletor a base.get_deleter<counting_deleter>()qualquer momento.

    No construtor de cópia, semelhante à solução single-threaded, você verificaria:

    counting_deleter* deleter = other.get_deleter<counting_deleter>();
    std::size_t expected = deleter->use_count.load(std::memory_order::relaxed);
    do {
        if (expected >= N) {
            // increasing the use_count would exceed the limit
            throw /* ... */;
        }
        // otherwise, attempt to increment atomically using a weak CAS
    } while (!deleter->use_count.compare_exchange_weak(expected, expected + 1));
    // note: the CAS can probably use std::memory_order::relaxed too
    

    Basicamente, verificamos se é possível aumentar a contagem de uso. Caso contrário, lançamos, caso contrário, tentamos incrementar use_countwith compare_exchange_weak.

    O operador de atribuição de cópia é análogo. As outras funções-membro especiais podem ser padronizadas, uma vez que mover ou destruir não pode ultrapassar o limite.

    • 4
  2. DeerSpotter
    2024-01-10T06:06:22+08:002024-01-10T06:06:22+08:00

    O padrão std::shared_ptr em C++ não fornece diretamente um mecanismo para definir uma contagem máxima de referência em tempo de compilação. No entanto, você pode obter um comportamento semelhante criando um ponteiro inteligente personalizado com uma contagem de referência limitada. Aqui está um exemplo básico:

    #include <iostream>
    #include <memory>
    #include <stdexcept>
    
    template <typename T, int MaxRefCount = 2>
    class LimitedSharedPtr {
    public:
        LimitedSharedPtr(T* ptr) : ptr_(ptr), refCount_(1) {
            if (refCount_ > MaxRefCount) {
                throw std::runtime_error("Exceeded maximum reference count");
            }
        }
    
        LimitedSharedPtr(const LimitedSharedPtr& other) : ptr_(other.ptr_), refCount_(other.refCount_) {
            if (refCount_ > MaxRefCount) {
                throw std::runtime_error("Exceeded maximum reference count");
            }
            refCount_++;
        }
    
        LimitedSharedPtr& operator=(const LimitedSharedPtr& other) {
            if (this != &other) {
                release();
                ptr_ = other.ptr_;
                refCount_ = other.refCount_;
                if (refCount_ > MaxRefCount) {
                    throw std::runtime_error("Exceeded maximum reference count");
                }
                refCount_++;
            }
            return *this;
        }
    
        ~LimitedSharedPtr() {
            release();
        }
    
        T* get() const {
            return ptr_;
        }
    
    private:
        void release() {
            refCount_--;
            if (refCount_ == 0) {
                delete ptr_;
            }
        }
    
        T* ptr_;
        int refCount_;
    };
    
    int main() {
        LimitedSharedPtr<double, 2> p(new double(2159));
        LimitedSharedPtr<double, 2> q = p; // works fine
        LimitedSharedPtr<double, 2> err1 = p; // throws runtime_error
        LimitedSharedPtr<double, 2> err2 = q; // throws runtime_error
    
        return 0;
    }
    
    • -2

relate perguntas

  • Por que os compiladores perdem a vetorização aqui?

  • Erro de compilação usando CMake com biblioteca [fechada]

  • Erro lançado toda vez que tento executar o premake

  • Como criar um tipo de octeto semelhante a std::byte em C++?

  • Somente operações bit a bit para std::byte em C++ 17?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle?

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Quando devo usar um std::inplace_vector em vez de um std::vector?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Marko Smith

    Estou tentando fazer o jogo pacman usando apenas o módulo Turtle Random e Math

    • 1 respostas
  • Martin Hope
    Aleksandr Dubinsky Por que a correspondência de padrões com o switch no InetAddress falha com 'não cobre todos os valores de entrada possíveis'? 2024-12-23 06:56:21 +0800 CST
  • Martin Hope
    Phillip Borge Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle? 2024-12-12 20:46:46 +0800 CST
  • Martin Hope
    Oodini Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores? 2024-12-12 06:27:11 +0800 CST
  • Martin Hope
    sleeptightAnsiC `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso? 2024-11-09 07:18:53 +0800 CST
  • Martin Hope
    The Mad Gamer Quando devo usar um std::inplace_vector em vez de um std::vector? 2024-10-29 23:01:00 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST
  • Martin Hope
    MarkB Por que o GCC gera código que executa condicionalmente uma implementação SIMD? 2024-02-17 06:17:14 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve