J'ai un compteur atomique ( std :: atomic
#include <iostream>
#include <atomic>
#include <limits>
#include <stdexcept>
#include <thread>
std::atomic<uint16_t> count;
uint16_t get_val() // called by multiple threads
{
uint16_t my_val;
do
{
my_val = count;
// make sure I get the next value
if (count.compare_exchange_strong(my_val, my_val + 1))
{
// if I got the next value, make sure we don't overflow
if (my_val == std::numeric_limits<uint16_t>::max())
{
count = std::numeric_limits<uint16_t>::max() - 1;
throw std::runtime_error("count overflow");
}
break;
}
// if I didn't then check if there are still numbers available
if (my_val == std::numeric_limits<uint16_t>::max())
{
count = std::numeric_limits<uint16_t>::max() - 1;
throw std::runtime_error("count overflow");
}
// there are still numbers available, so try again
}
while (1);
return my_val + 1;
}
void run()
try
{
while (1)
{
if (get_val() == 0)
exit(1);
}
}
catch(const std::runtime_error& e)
{
// overflow
}
int main()
{
while (1)
{
count = 1;
std::thread a(run);
std::thread b(run);
std::thread c(run);
std::thread d(run);
a.join();
b.join();
c.join();
d.join();
std::cout << ".";
}
return 0;
}
3 Réponses :
Il me semble qu'il y a toujours une condition de course où supposons que comptage code> sera défini sur 0 de manière momentanément telle qu'un autre thread verra la valeur 0. p>
compte code> est à
std :: numeric_limits
comte.compare_exchange_strong (my_val, my_val + 1) code>, le nombre est défini sur 0 et c'est ce que le thread 2 verra si cela arrive à appeler et à compléter
get_val ( ) code> Avant le thread 1 a une chance de restaurer
compter code> à
max () code>. P>
Si l'efficacité est une préoccupation importante, je ne suggérerai pas d'être si stricte sur le chèque. Je suppose que dans le dépassement de l'utilisation normale ne sera pas un problème, mais avez-vous vraiment besoin de la gamme complète de 65k (votre exemple utilise UINT16)?
Il serait plus facile si vous supposez un maximum sur le nombre de threads que vous avez avoir couru. Ceci est une limite raisonnable car aucun programme n'a un nombre illimité de concurrence. Donc, si vous avez des threads N code>, vous pouvez simplement réduire votre limite de dépassement à
65K - N Code>. Pour comparer si vous débordez vous n'avez pas besoin d'un CAS: P>
uint16_t current = count.load(std::memory_order_relaxed);
if( current >= (std::numeric_limits<uint16_t>::max() - num_threads - 1) )
throw std::runtime_error("count overflow");
count.fetch_add(1,std::memory_order_relaxed);
Je viens d'utiliser uint16_t code> si un trop-plein potentiel se produit plus tôt. J'aime votre idée, mais le nombre de threads est maintenant connu et la dernière chose que je veux faire est de choisir une limite supérieure magique, puis de le faire dépasser à un moment donné de l'avenir.
@lori, le nombre peut ne pas être connu, mais il doit y avoir une limite supérieure raisonnable. Vous pouvez spécifier 1000 threads, voire 10k si vous le souhaitez. Ou utilisez un uint_32 code>, puis spécifie un million.
Oui, vous devez utiliser CAS CODE> OPERATION.
std::atomic<uint16_t> g_count;
uint16_t get_next() {
uint16_t new_val = 0;
do {
uint16_t cur_val = g_count; // 1
if (cur_val == std::numeric_limits<uint16_t>::max()) { // 2
throw std::runtime_error("count overflow");
}
new_val = cur_val + 1; // 3
} while(!std::atomic_compare_exchange_weak(&g_count, &cur_val, new_val)); // 4
return new_val;
}
uint64_t et problème résolu? :)