如何在c ++中阻止函数从外部运行

时间:2016-09-21 14:54:43

标签: c++ c++11 c++14

我想运行一个函数并判断函数是否在n毫秒后没有完成,停止该函数并启动另一个函数。类似这样的代码:

void run()
{
    //do something that doesn't have while(1)
}


void main()
{
    run();
    if(runFunctionDidntFinishInSeconds(10)
    {
        endPrintFunction();
        backupPlan();
    }
    return 0;
}

我搜索了一下,发现了boost :: timed_join函数。这是我的代码:

void run()
{
    int a;
    for (int i = 0; i < 2000; i++)
        cout << i << endl;
}


int main()
{
    boost::thread t = new boost::thread(&run);
    if (t.timed_join(boost::posix_time::microseconds(10000))){
        cout << "done" << endl;
    }
    else{
        cout << endl << "not done" << endl;
    }


    system("pause");
    return 0;
}

但它不会阻止线程't'运行。我去了终止线程,但这不是一个好选择。 我想要'a'函数来完成我告诉它的确切时间。  系统每16ms输入一次,我想对其进行处理,并说如果处理时间超过13ms,请离开并进行备份计划。我希望它从编写处理方法的人那里抽象出来。因此,在它的顶部放置一个while循环会让我感到延迟。 我该怎么办? 至少我认为我需要的是能够重置处理线程以执行它需要再做的事情!

5 个答案:

答案 0 :(得分:2)

我认为你正在寻找像std :: future。

这样的东西

http://en.cppreference.com/w/cpp/thread/future/wait_for

您可以在另一个线程中启动该函数,并等待该函数返回或超时。

对于你的例子:

std::future< void > future = std::async( std::launch::async, print );

auto status = future.wait_for( std::chrono::seconds( 10 ) );
if ( status == std::future_status::deferred )
{
    std::cout << "deferred\n";
}
else if ( status == std::future_status::timeout )
{
    std::cout << "timeout\n";
}
else if ( status == std::future_status::ready )
{
    std::cout << "ready!\n";
}

然而,这并不会导致分离的线程结束。为此,必须在启动时包含一个标志,因此分离的线程可以自行清理并保存。

void run(const std::atomic_bool& cancelled)
{
    int a;
    for (int i = 0; i < 2000; i++)
    {
        cout << i << endl;
        if (cancelled)
            return;
    }
}

std::atomic_bool cancellation_token = false;
std::future< void > future = std::async( std::launch::async, 
                                         run,
                                         std::ref(cancellation_token) );

auto status = future.wait_for( std::chrono::seconds( 10 ) );
if ( status == std::future_status::deferred )
{
    std::cout << "deferred\n";
}
else if ( status == std::future_status::timeout )
{
    std::cout << "timeout\n";
    cancellation_token = true;
}
else if ( status == std::future_status::ready )
{
    std::cout << "ready!\n";
}

答案 1 :(得分:1)

  

我希望从编写处理方法的人那里抽象出来。

标准C ++没有办法从该函数的调用图外部强行中断函数的控制流(它调用的函数可以抛出,但有人不能为它们抛出

特定于操作系统的线程系统有终止线程的方法。但是,这会使程序可能处于未定义状态,因为尚未调用任何堆栈变量的析构函数。因为当你杀死它时你不知道它在那个处理中的位置,你就无法在它之后有效地清理它。即使是C程序也不能保证可以终止任意函数;它必须是一个不动态分配内存或其他必须清理的资源的。

您可以通过非常仔细地编写您的功能来弥补这一点。但这需要编写该功能的人非常小心地编码。因此,没有抽象,因为编写函数的人必须知道规则是什么,并且需要遵循它们。

所以唯一有效的解决方案需要合作。该函数必须以这样的方式编写,即可以通过那些依赖于操作系统的功能安全地停止,或者必须编写该函数以定期检查某个值并停止本身

答案 2 :(得分:0)

以下是两种和3/4种方法。

第一个要求您要停止的代码合作。它可以在运行时轮询某个变量,也可以定期调用一个函数,该函数可以抛出异常来暂停执行。 boost可中断的线程遵循第二个模型。

第二个要求您启动新进程,将数据编组到函数中,然后使用IPC获取信息。如果函数没有及时返回,则会终止子进程。

第三个“半”包括用不同的语言重写代码,或者使用C ++作为脚本语言。您可以在为您执行第一个或第二个解决方案的解释器中运行代码。

现在,一个实用的替代方案(1/4解决方案)是确保该函数是纯函数的,在一个带有半可靠中止消息的单独线程中运行它(如第一个),并丢弃其返回值如果需要太长时间。这不是你想要的,但更容易。

答案 3 :(得分:0)

有一种方法可以将原子用作信号量,但是由于load每次迭代都会发出完全内存障碍,从而降低性能:

#include <iostream>
#include <thread>
#include <chrono>
#include <atomic>

std::atomic<bool> printFinished { false };
std::atomic<bool> shouldPrintRun { true };

void print()
{
    while (shouldPrintRun.load() /* && your normal stop condition*/)
    {
        //work..
    }
    printFinished.store(true);
}


int main()
{
    std::thread t(print);
    std::this_thread::sleep_for(std::chrono::seconds(10));
    if (!printFinished.load())
    {
        shouldPrintRun.store(false);
        t.join();
        std::cout << "help!";
    }
    return 0;
}

如果您不希望在另一个线程上运行的函数检查是否需要停止,那么终止该线程只是 选项。

答案 4 :(得分:0)

一个可能的解决方案是,你必须将冗长的功能变成小型和小型的。短增量功能,每次从上一次调用时仍然可以继续执行任务。可以在线程中运行的下面的代码将执行时间限制器的类似工作,并且可以随意终止。

void Process()
{       
    bool flag = true;
    while (running)
    {
        std::chrono::high_resolution_clock::time_point time1 = std::chrono::high_resolution_clock::now();
        std::chrono::milliseconds span(16);
        while ( (std::chrono::high_resolution_clock::now() - time1 ) < span)
        {
            flag ? incremental_function1() : incremental_function2();

            if (!running) return;
        }
        flag = (!flag);
    }
}