web-dev-qa-db-fra.com

Comment implémenter le délai d'expiration de la fonction en c ++

J'ai la fonction f; Je veux lever l'exception 1 après le début f. Je ne peux pas modifier f (). Est-il possible de le faire en c ++?

try {
   f();
}
catch (TimeoutException& e) {
//timeout
}
10
Newbie

Vous pouvez créer un thread séparé pour exécuter l'appel lui-même et attendre une variable de condition dans votre thread principal qui sera signalée par le thread effectuant l'appel à f une fois qu'il reviendra. L'astuce consiste à attendre la variable de condition avec votre délai d'expiration de 1, de sorte que si l'appel prend plus de temps que le délai, vous vous réveillerez, en serez informé et pourrez lever l'exception - le tout dans le thread principal. Voici le code (démo live ici ):

#include <iostream>
#include <chrono>
#include <thread>
#include <mutex>
#include <condition_variable>

using namespace std::chrono_literals;

int f()
{
    std::this_thread::sleep_for(10s); //change value here to less than 1 second to see Success
    return 1;
}

int f_wrapper()
{
    std::mutex m;
    std::condition_variable cv;
    int retValue;

    std::thread t([&cv, &retValue]() 
    {
        retValue = f();
        cv.notify_one();
    });

    t.detach();

    {
        std::unique_lock<std::mutex> l(m);
        if(cv.wait_for(l, 1s) == std::cv_status::timeout) 
            throw std::runtime_error("Timeout");
    }

    return retValue;    
}

int main()
{
    bool timedout = false;
    try {
        f_wrapper();
    }
    catch(std::runtime_error& e) {
        std::cout << e.what() << std::endl;
        timedout = true;
    }

    if(!timedout)
        std::cout << "Success" << std::endl;

    return 0;
}
12
Smeeheey

Vous pouvez également utiliser std :: packaged_task pour exécuter votre fonction f() dans un autre thread. Cette solution est plus ou moins similaire à this = un, seulement qu'il utilise des classes standard pour conclure.

std::packaged_task<void()> task(f);
auto future = task.get_future();
std::thread thr(std::move(task));
if (future.wait_for(1s) != std::future_status::timeout)
{
   future.get(); // this will propagate exception from f() if any
}
else
{
   thr.detach(); // we leave the thread still running
   throw std::runtime_error("Timeout");
}
3
Alex Che

Vous pouvez créer un nouveau thread et attendre de manière asynchrone le passage de 1, puis lever une exception. Cependant, les exceptions ne peuvent être interceptées que dans le même thread où elles sont levées, vous ne pouvez donc pas intercepter dans le même thread où vous avez appelé f(), comme dans votre exemple de code - mais ce n'est pas une exigence déclarée, il peut donc être OK pour vous.

Ce n'est que si f est garanti de retourner en moins de 1s, que vous pouvez le faire de manière synchrone:

  • stocker l'heure actuelle
  • appeler f()
  • attendre l'heure actuelle - temps enregistré + 1s

Mais il peut être assez difficile de prouver que f revient en fait à temps.

1
eerorika