如果在5秒内未再次调用FunctionA,则调用FunctionB

时间:2016-10-27 08:17:57

标签: c++ timeout

我正在尝试编写一个函数TimeoutFunction,它调用另一个函数e.x. printf,如果在x秒内未再次调用函数TimeoutFunction。如果再次调用,则应重置超时。 这段代码例如:

void TimeoutFunction(string MyString)
{
    //Wait 5 seconds and then call printf(MyString)
}

int main()
{
    TimeoutFunction("A");
    Sleep(4);
    TimeoutFunction("B");
    Sleep(6);
    TimeoutFunction("C");
    Sleep(10);
    TimeoutFunction("D");
    Sleep(2);
    TimeoutFunction("E");
}

会打印: BCE或至少BC

参数MyString不是必需的,但我添加了它以使其可视化。

2 个答案:

答案 0 :(得分:0)

我写了一些不太好的代码,但它可以正常工作

#include <thread>
#include <windows.h>

bool running;
unsigned long StartTime;
unsigned int WaitTime = 500;
const char* PrintString;
std::thread InternThread;

void MyPrint()
{
    while (running)
    {
        auto WaitedTime = GetTickCount() - StartTime;
        if (WaitedTime > WaitTime)
        {
            printf(PrintString);
            break;
        }
        Sleep(10);
    }
}


void TimeoutFunction(const char* MyString)
{
    StartTime = GetTickCount();
    PrintString = MyString;
    running = false;
    if (InternThread.joinable())
    {
        InternThread.join();
    }

    running = true;
    InternThread = std::thread(MyPrint);
}

int main()
{
    TimeoutFunction("A");
    Sleep(400);
    TimeoutFunction("B");
    Sleep(600);
    TimeoutFunction("C");
    Sleep(1000);
    TimeoutFunction("D");
    Sleep(200);
    TimeoutFunction("E");

    InternThread.join();
    return 0;
}

如果某人有更好的代码,欢迎您。

答案 1 :(得分:0)

我根据your self-answer编写了等效的有希望的可移植代码。

但首先:

您在自己的答案中使用的技术是明智的,确保主线程或工作线程随时访问公共共享变量。这不是我第一次想到如何做到这一点,即它对我来说并不明显,它使代码比使用互斥锁确保对这些变量的独占访问更简单,更有效。但是,编码方式存在两个同步问题:

  • bool标志running需要设置为线程安全的 如果该标志不是线程安全的,那么在一个线程(例如主线程)中进行的更改可能只是将其发送到某个缓存,但不是一直到主内存,同样,另一个线程的检查可能只是检查缓存,而不是直接主内存。三个可能性是std::atomic<bool>std::atomic_flag(不太方便,但保证无锁定),第三,使用额外的std::mutex,例如结合std::unique_lock

  • TimeoutFunction函数中,共享状态更新需要在加入线程后进行
    为了完善,GetTickCount结果应该在等待线程加入之前存储在局部变量中。

对于标准的C ++希望可移植的代码,main函数就像这样(与你发布的答案几乎完全相同):

main.cpp中:
#include <stdio.h>                  // printf
#include <Delayed_action.hpp>       // my::(Delayed_action, sleep)

// Alternative to defining lots of small lambdas, a special case functor:
struct Print
{
    char const* s; 
    void operator()() const { printf( "%s", s ); }
    Print( char const* const a_literal ): s( a_literal ) {}
};

auto main()
    -> int
{
    using my::Delayed_action; using my::sleep;
    using namespace std::literals;  // ms

    Delayed_action da;
    da.set_action( Print{ "A" } );
    sleep( 400ms );
    da.set_action( Print{ "B" } );
    sleep( 600ms );
    da.set_action( Print{ "C" } );
    sleep( 1000ms );
    da.set_action( Print{ "D" } );
    sleep( 200ms );
    da.set_action( Print{ "E" } );
    da.wait_for_action_completed();
    printf( "\n" );
}

这里支持重用的两个主要抽象是

  • 将线程通信状态放在对象而不是全局中。

  • 参数化操作,而不是硬编码操作。

Delayed_action的实现使用了一些常规支持:

CPPX级-kinds.hpp:
#pragma once

namespace cppx {
    class No_copy
    {
    private:
        auto operator=( No_copy const& ) -> No_copy& = delete;
        No_copy( No_copy const& ) = delete;

    public:
        auto operator=( No_copy&& ) -> No_copy& { return *this; }
        No_copy() {}
        No_copy( No_copy&& ) {}
    };

    class No_move
    {
    private:
        auto operator=( No_move&& ) -> No_move& = delete;
        No_move( No_move&& ) = delete;

    public:
        auto operator=( No_move const& ) -> No_move& { return *this; }
        No_move() {}
        No_move( No_move const& ) {}
    };

    class No_copy_or_move
        : public No_copy
        , public No_move
    {};
}  // namespace cppx
CPPX-threading.hpp:
#pragma once
#include <cppx-class-kinds.hpp>     // cppx::No_copy_or_move

#include <atomic>           // std::atomic
#include <chrono>           // std::chrono::milliseconds
#include <thread>           // std::thread

namespace cppx {
    namespace this_thread = std::this_thread;

    inline namespace std_aliases {
        using Milliseconds      = std::chrono::milliseconds;
        using Steady_clock      = std::chrono::steady_clock;
        using Time_point        = std::chrono::time_point<Steady_clock>;

        using Thread            = std::thread;    
    }

    inline void sleep( Milliseconds const duration )
    {
        this_thread::sleep_for( duration );
    }

    // Syntactic sugar for std::atomic_flag:
    // • boolean assignment
    // • default init to false.
    // std::atomic_flag is guaranteed lock free, as opposed to std::atomic<bool>.
    // Cost: there's no way to check the value except by setting it to true.
    class Atomic_flag
        : public No_copy_or_move
    {
    private:
        std::atomic_flag    flag_   = ATOMIC_FLAG_INIT; // Initialized to false.

    public:
        void clear() { flag_.clear(); }

        auto test_and_set() -> bool
        {
            bool const previous_value = flag_.test_and_set();
            return previous_value;
        }

        void set() { test_and_set(); }

        void operator=( bool const should_be_set )
        {
            if( should_be_set ) set(); else clear();
        }

        Atomic_flag() {}
    };
}  // namespace cppx

通过对标准库的事物进行包装和重命名,基于标准库的重新实现您的想法,Delayed_action类,可以如下所示:

Delayed_action.hpp:
#pragma once
#include <cppx-class-kinds.hpp> // cppx::No_copy_or_move
#include <cppx-threading.hpp>   // cppx::(Atomic_flag, sleep)

#include <functional>           // std::(function, ref)
#include <utility>              // std::move

namespace my {
    using namespace cppx::std_aliases;
    using namespace std::literals;

    using cppx::Atomic_flag;
    using cppx::No_copy_or_move;
    using cppx::sleep;
    using std::move;
    using std::ref;

    using Action = std::function<void()>;

    class Delayed_action
        : public No_copy_or_move
    {
    private:
        struct Parameters
        {
            Atomic_flag     run;
            Action          action;
            Time_point      when;
        };

        static void polling( Parameters& parameters )
        {
            for( ;; )
            {
                if( not parameters.run.test_and_set() )
                {
                    return;
                }
                else if( Steady_clock::now() >= parameters.when )
                {
                    parameters.action();
                    return;
                }
                sleep( 10ms );
            }
        }

    private:
        Parameters  parameters_;
        Thread      worker_;

        void join_worker_thread()
        {
            if( worker_.joinable() )
            {
                worker_.join();
            }
        }

        void end_worker_thread()
        {
            parameters_.run = false;
            join_worker_thread();
        }

    public:
        static auto default_delay() -> Milliseconds { return 500ms; }

        void set_action( Action action, Milliseconds const delay = default_delay() )
        {
            Time_point const when = Steady_clock::now() + delay;
            end_worker_thread();
            parameters_.action = move( action );
            parameters_.when = when;
            parameters_.run = true;
            worker_ = Thread( &polling, ref( parameters_ ) );
        }

        void wait_for_action_completed() { join_worker_thread(); }

        ~Delayed_action() { end_worker_thread(); }

        Delayed_action() {}

        Delayed_action( Action action, Milliseconds const delay = default_delay() )
        {
            set_action( move( action ), delay );
        }
    };
}  // namespace my