线程阻塞代码c++的main()

ss2ws0br  于 2023-01-22  发布在  其他
关注(0)|答案(1)|浏览(153)

我正在尝试为wait()方法实现线程,这样它就不会阻塞main()线程。
在主线程中,wait()函数必须每小时运行一次validate函数。因此,当wait()函数运行时,main()线程被阻塞,并且不会继续执行任何代码,直到wait()函数完成或线程被加入。因此,我想独立于main()运行wait()。
main.cpp

#include <iostream>
#include <fstream>
#include <time.h>
#include <sys/stat.h>
#include <boost/date_time.hpp>
#include <boost/thread/thread.hpp> 
#include <boost/filesystem.hpp>
#include <boost/thread.hpp>
#include <pthread.h>

using namespace std;

constexpr char clock_file[] = "/home/saman/Desktop/clock";
constexpr char sync_file[] = "/home/saman/Desktop/hi/hi/synchronized";

class TimeEvent {
public:
     void receive_time_event(timespec& time) {
        // Create clock file
        ofstream clockFile(clock_file);
        if (!clockFile.is_open()) {
            cout << "Failed to open file" << endl;
        }
        clockFile.close();
        // Create synchronized file
        boost::filesystem::path dir(sync_file);
        boost::filesystem::create_directories(dir.parent_path());
        ofstream synchronizedFile(sync_file);
        if (!synchronizedFile.is_open()) {
            cout << "Failed to open file" << endl;
        }
        synchronizedFile.close();
    }

    timespec get_latest_clock_entry(){
    cout << "hello" << endl;
}
    void wait(){
        while(1){
        cout << "start wait" << endl;
        auto Now_Time = boost::posix_time::second_clock::local_time();
        auto update_Time = Now_Time + boost::posix_time::minutes(1);
        auto counter = update_Time - Now_Time;
        boost::this_thread::sleep(counter);
        validate();
        }
    }

    void validate(){
        // cout << "hi" << endl;
        timespec currentTime;  
        timespec_get(&currentTime, TIME_UTC);
        timespec ModifiedTime = get_latest_clock_entry();
        if (currentTime.tv_sec > ModifiedTime.tv_sec){
            ofstream clockfile(clock_file);
            if (!clockfile.is_open()) {
            cout << "Failed to open file" << endl;
        }
        clockfile.close();
        }
        // update system time to match the clock file's modified time
        int result = clock_settime(CLOCK_REALTIME, &ModifiedTime);
        if(result == 0){
            cout<< "updated system time to current time" << endl;
        }
        else{
            cout<< "failed to update system time" << endl;
        }
    }
    void update_system_time_on_startup() {
           cout << "hello" << endl;
};

int main() {
    TimeEvent timeEvent;
    timespec time;
    timespec_get(&time, TIME_UTC);
    cout << "hello" << endl;
    timeEvent.receive_time_event(time);
    auto lastModifiedTime = timeEvent.get_latest_clock_entry();
    boost::thread Thread(&TimeEvent::wait, &timeEvent); 
    // boost::thread Thread1(boost::bind(&TimeEvent::wait, &timeEvent));
    cout << "good" << endl;
    timeEvent.update_system_time_on_startup();
    Thread.join();
    return 0;
}
56lgkhnf

56lgkhnf1#

所以我想独立于main()运行wait()
你可以通过在一个单独的线程中运行wait来实现,当然你已经这样做了,而且你 * 可以 * 实际上已经在main中运行了其他代码,你已经这样做了,尽管很少:

cout << "good" << endl;

wait在你的线程上运行的时候打印出来的。一切都很好。当然,如果你想做更多的事情,那就去做吧。一旦你join()Thread,你的主线程就会阻塞,直到线程退出。
让我来介绍一下我在代码审查期间发现的一些观察结果,然后给您一些关于后台线程的优雅关闭的想法。

第一部分观察

程序的行为与预期不符的一个原因是get_latest_clock_entry()中的Undefined Behavior

timespec get_latest_clock_entry() {
    std::cout << "hello" << std::endl;
    return {}; // TODO, no return is Undefined Behavior!
}

任何事情都有可能发生,但依赖条件currentTime.tv_sec > ModifiedTime.tv_sec肯定不可靠。
此外,在时钟动态调整的系统中,基于时钟值进行等待可能不可靠,就像您的代码对::clock_settime(CLOCK_REALTIME)所做的那样。当然,正如所写的那样,代码的行为将不稳定,因为您确实使用具有 * 不确定值 * 的clock_settime(如前所述)。
在线程的开始和你的update_system_time_on_startup之间也有一个竞态条件。目前,显然它什么也不做(只是说"hello"),但是一旦它做了更多的事情(像它说的那样),它(a)在没有同步的情况下接触共享状态(b)影响wait的定时代码。
receive_time_event奇怪地忽略了这个参数。这是故意的吗?为什么是通过lvalue-reference传递的?它需要更新吗?

建议

你可能应该更好地解释你实际上想达到的目标。
假设您尝试同步不同的系统时钟,如下所示

if (currentTime.tv_sec > ModifiedTime.tv_sec)
    touch(clock_file);

旨在每分钟一次:

  • 如果"我们的实时时钟"比来自其他来源的"某个时钟条目"提前〉1s,则更新clock_file
  • 无条件地将我们时钟设置为等于"其他源"

这已经不符合问题中的问题描述。另外,您的等待代码应该
1.使用单调时钟以防止在改变时钟时出现错误
1.使用预先计算的最后期限,而不是盲目地在给定时间上增加60秒
1.我不太在意UTC与本地时间
让我们回顾并简化一些其他代码:

static void touch(fs::path p) {
    create_directories(p.parent_path());
    fs::ofstream ofs(p);
    if (!ofs)
        throw std::runtime_error("Failed to create " + p.native());
}

static ::timespec getCurrentTime() {
    timespec currentTime;
    timespec_get(&currentTime, TIME_UTC);
    return currentTime;
}

void validate() {
    // std::cout << "hi" << std::endl;
    timespec currentTime  = getCurrentTime();
    timespec ModifiedTime = get_latest_clock_entry();

    if (currentTime.tv_sec > ModifiedTime.tv_sec)
        touch(clock_file);

    // update system time to match the clock file's modified time
    if (0 != clock_settime(CLOCK_REALTIME, &ModifiedTime))
        throw std::system_error(make_error_code(static_cast<std::errc>(errno)));
}

要消除争用条件,只需将update_system_time_on_startup移到timeEvent类中,更好的是,将整个示例移到线程中!

void synchronizationThread() {
    try {
        TimeEvent timeEvent;
        timeEvent.update_system_time_on_startup();
        timeEvent.wait();
    } catch (std::exception const& e) {
        std::cerr << "synchronizationThread error: " << e.what() << std::endl;
    }
}

int main() {
    boost::thread sync(synchronizationThread);

    sync.join();
}

如前所述,等待代码过于复杂(清理版本):

void wait() {
    while (1) {
        std::cout << "start wait" << std::endl;
        ptime Now_Time    = boost::posix_time::second_clock::local_time(),
              update_Time = Now_Time + boost::posix_time::minutes(1);

        boost::posix_time::time_duration counter = update_Time - Now_Time;
        std::cerr << "Sleeping for " << counter << "\n";
        boost::this_thread::sleep(counter);
        boost::this_thread::sleep_for(60s);
        validate();
    }
}

它可以更简单,同时又是等价的:

void wait() {
    while (1) {
        std::cout << "start wait" << std::endl;
        std::this_thread::sleep_for(60s);
        validate();
    }
}

注意,我们现在不再需要posix_time,也不需要链接到Boost Datetime库,事实上,我们可以用std::thread和std::filesystem替换boost::thread和boost::filesystem,并且be * 没有任何boost依赖性 *。
当然,这里假设validate()不需要时间,或者后续的validates()间隔超过60秒也没关系,坦白地说,在时间同步听起来很重要的情况下,这听起来很奇怪,所以我会写 * 我的意思 *,明确地使用一个单调的时钟:

void wait() {
    using Clock   = std::chrono::steady_clock; // monotonous!

    auto next_cycle = [start = Clock::now()] {
        auto deadline = start;
        while (deadline <= Clock::now())
            deadline += cycle_interval;
        std::this_thread::sleep_until(deadline);
        return true;
    };

    while (next_cycle()) {
        validate();
    }
}

显然,定义适当的区间如下:

constexpr auto cycle_interval = 60s;

现场演示

    • 第一个e第一个f第一个x
#include <cassert>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <thread>

#include <sys/stat.h>
#include <time.h>

namespace fs = std::filesystem;
using namespace std::chrono_literals;

constexpr auto& clock_file     = "/home/saman/Desktop/clock";
constexpr auto& sync_file      = "/home/saman/Desktop/hi/hi/synchronized";
constexpr auto  cycle_interval = 5s; // 60s

class TimeEvent {
    static void touch(fs::path p) {
        create_directories(p.parent_path());
        std::ofstream ofs(p);
        if (!ofs)
            throw std::runtime_error("Failed to create " + p.native());
    }
    static ::timespec getCurrentTime() {
        timespec currentTime;
        timespec_get(&currentTime, TIME_UTC);
        return currentTime;
    }

  public:
    void receive_time_event(timespec /*time*/) {
        touch(clock_file);
        touch(sync_file);
    }

    timespec get_latest_clock_entry() { return getCurrentTime(); }

    void wait() {
        using Clock = std::chrono::steady_clock; // monotonous!

        auto next_cycle = [start = Clock::now()] {
            auto deadline = start;
            while (deadline <= Clock::now())
                deadline += cycle_interval;

            std::cout << "Waiting " << (deadline - Clock::now()) / 1ms << "ms" << std::endl;
            std::this_thread::sleep_until(deadline);
            return true;
        };

        while (next_cycle()) {
            validate();
        }
    }

    void validate() {
        std::cout << "validate()" << std::endl;
        timespec currentTime  = getCurrentTime();
        timespec ModifiedTime = get_latest_clock_entry();

        if (currentTime.tv_sec > ModifiedTime.tv_sec)
            touch(clock_file);

        {
            // I don't want to risk messing up my systemclock while doing the testing
            auto tie = [](timespec const& ts) { return std::tie(ts.tv_sec, ts.tv_nsec); };
            assert(tie(currentTime) <= tie(ModifiedTime));
        }

        // update system time to match the clock file's modified time
        if (0 != clock_settime(CLOCK_REALTIME, &ModifiedTime))
            throw std::system_error(make_error_code(static_cast<std::errc>(errno)));
    }

    void update_system_time_on_startup() {
        std::cout << "update_system_time_on_startup" << std::endl;
        receive_time_event(getCurrentTime());
    };
};

void synchronizationThread() {
    try {
        TimeEvent timeEvent;
        timeEvent.update_system_time_on_startup();
        timeEvent.wait();
    } catch (std::exception const& e) {
        std::cerr << "synchronizationThread error: " << e.what() << std::endl;
    }
}

int main() {
    std::jthread sync(synchronizationThread);

    for (int i = 0; i < 27; ++i) {
        std::this_thread::sleep_for(1s);
        std::cout << "." << std::flush;
    }
}

本地:

额外奖励:正常关机

当然,您希望在合理的时间限制内正常关机:

    • 一个月一次**
#include <atomic>
#include <cassert>
#include <condition_variable>
#include <filesystem>
#include <fstream>
#include <functional>
#include <iostream>
#include <mutex>
#include <thread>

#include <sys/stat.h>
#include <time.h>

namespace fs = std::filesystem;
using namespace std::chrono_literals;

constexpr auto& clock_file     = "/home/saman/Desktop/clock";
constexpr auto& sync_file      = "/home/saman/Desktop/hi/hi/synchronized";
constexpr auto  cycle_interval = 5s; // 60s

class TimeEvent {
    static void touch(fs::path p) {
        create_directories(p.parent_path());
        std::ofstream ofs(p);
        if (!ofs)
            throw std::runtime_error("Failed to create " + p.native());
    }
    static ::timespec getCurrentTime() {
        timespec currentTime;
        timespec_get(&currentTime, TIME_UTC);
        return currentTime;
    }

    using Clock = std::chrono::steady_clock; // monotonous clock
    Clock::time_point const start = Clock::now();
    std::mutex              mx;
    std::condition_variable cv;
    std::atomic_bool        shutdown_requested{false};

    bool next_cycle() {
        auto deadline = start;
        while (deadline <= Clock::now())
            deadline += cycle_interval;

        std::cout << "Waiting " << (deadline - Clock::now()) / 1ms << "ms" << std::endl;

        std::unique_lock<std::mutex> lk(mx);
        cv.wait_until(lk, deadline, [this] { return shutdown_requested.load(); });
        return !shutdown_requested;
    };

  public:
    void shutdown() {
        shutdown_requested = true;
        std::lock_guard<std::mutex> lk(mx);
        cv.notify_all();
    }

    void receive_time_event(timespec /*time*/) {
        touch(clock_file);
        touch(sync_file);
    }

    timespec get_latest_clock_entry() { return getCurrentTime(); }

    void wait() {
        try {
            update_system_time_on_startup(); // optionally?

            while (next_cycle()) {
                validate();
            }
        } catch (std::exception const& e) {
            std::cerr << "synchronizationThread error: " << e.what() << std::endl;
        }
    }

    void validate() {
        std::cout << "validate()" << std::endl;
        timespec currentTime  = getCurrentTime();
        timespec ModifiedTime = get_latest_clock_entry();

        if (currentTime.tv_sec > ModifiedTime.tv_sec)
            touch(clock_file);

        {
            // I don't want to risk messing up my systemclock while doing the testing
            auto tie = [](timespec const& ts) { return std::tie(ts.tv_sec, ts.tv_nsec); };
            assert(tie(currentTime) <= tie(ModifiedTime));
        }

        // update system time to match the clock file's modified time
        if (0 != clock_settime(CLOCK_REALTIME, &ModifiedTime))
            throw std::system_error(make_error_code(static_cast<std::errc>(errno)));
    }

    void update_system_time_on_startup() {
        std::cout << "update_system_time_on_startup" << std::endl;
        receive_time_event(getCurrentTime());
    };
};

int main() {
    TimeEvent    te;
    std::jthread sync(&TimeEvent::wait, &te);

    for (int i = 0; i < 27; ++i) {
        std::this_thread::sleep_for(1s);
        std::cout << "." << std::flush;
    }

    te.shutdown();
}

BONUS2:使用加速线程中断点

这样做的优点是将TimeEvent示例封装在线程中,从而消除了手动同步的需要,缺点是需要Boost Chrono、Boost System、Boost Thread(在我的示例中,还需要Boost Filesystem,只是为了说明切换是多么容易):

    • 一个月三次**
#include <boost/chrono.hpp>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/thread.hpp>
#include <cassert>
#include <fstream>
#include <iostream>

#include <sys/stat.h>
#include <time.h>

namespace fs = boost::filesystem;

constexpr auto& clock_file     = "/home/saman/Desktop/clock";
constexpr auto& sync_file      = "/home/saman/Desktop/hi/hi/synchronized";
constexpr auto  cycle_interval = boost::chrono::seconds(5); // minutes(60)

class TimeEvent {
    static void touch(fs::path p) {
        create_directories(p.parent_path());
        fs::ofstream ofs(p);
        if (!ofs)
            throw std::runtime_error("Failed to create " + p.native());
    }
    static ::timespec getCurrentTime() {
        timespec currentTime;
        timespec_get(&currentTime, TIME_UTC);
        return currentTime;
    }

    using Clock = boost::chrono::steady_clock; // monotonous clock
    Clock::time_point const start = Clock::now();

    bool next_cycle() const {
        auto deadline = start;
        while (deadline <= Clock::now())
            deadline += cycle_interval;

        std::cout << "Waiting " << (deadline - Clock::now()) / boost::chrono::milliseconds(1) << "ms"
                  << std::endl;

        boost::this_thread::sleep_until(deadline);
        return true;
    };

  public:
    void shutdown() {
    }

    void receive_time_event(timespec /*time*/) {
        touch(clock_file);
        touch(sync_file);
    }

    timespec get_latest_clock_entry() { return getCurrentTime(); }

    void wait() {
        while (next_cycle()) {
            validate();
        }
    }

    void validate() {
        std::cout << "validate()" << std::endl;
        timespec currentTime  = getCurrentTime();
        timespec ModifiedTime = get_latest_clock_entry();

        if (currentTime.tv_sec > ModifiedTime.tv_sec)
            touch(clock_file);

        {
            // I don't want to risk messing up my systemclock while doing the testing
            auto tie = [](timespec const& ts) { return std::tie(ts.tv_sec, ts.tv_nsec); };
            assert(tie(currentTime) <= tie(ModifiedTime));
        }

        // update system time to match the clock file's modified time
        if (0 != clock_settime(CLOCK_REALTIME, &ModifiedTime))
            throw std::system_error(make_error_code(static_cast<std::errc>(errno)));
    }

    void update_system_time_on_startup() {
        std::cout << "update_system_time_on_startup" << std::endl;
        receive_time_event(getCurrentTime());
    };
};

void synchronizationThread() {
    try {
        TimeEvent timeEvent;
        timeEvent.update_system_time_on_startup();
        timeEvent.wait();
    } catch (std::exception const& e) {
        std::cerr << "synchronizationThread error: " << e.what() << std::endl;
    }
}

int main() {
    boost::thread sync(synchronizationThread);

    for (int i = 0; i < 27; ++i) {
        boost::this_thread::sleep_for(boost::chrono::seconds(1));
        std::cout << "." << std::flush;
    }

    if (sync.joinable()) {
        sync.interrupt();
        sync.join();
    }
}

你甚至可以让你永远不会忘记打断和加入:

int main() {
    boost::thread sync(synchronizationThread);
    boost::thread_guard<boost::interrupt_and_join_if_joinable> guard(sync);

    for (int i = 0; i < 27; ++i) {
        boost::this_thread::sleep_for(boost::chrono::seconds(1));
        std::cout << "." << std::flush;
    }
}

相关问题