You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为类Shell程序添加调度任务并实现DLL插件化?

实现无干扰的定时调度器及插件化方案

一、实现不干扰主循环的定时调度

方案1:后台线程实现

  • 思路:创建独立后台线程,线程内循环每隔5秒执行task(),主循环继续处理用户输入,两者并行运行。
  • 注意点:若task()涉及共享资源(如全局变量、控制台输出),需加锁保证线程安全,避免竞态条件。
  • 代码示例:
#include <iostream>
#include <thread>
#include <chrono>
#include <mutex>

std::mutex task_mutex;

void task() {
    std::lock_guard<std::mutex> lock(task_mutex);
    std::cout << "定时任务执行中..." << std::endl;
}

void scheduler_thread() {
    while (true) {
        std::this_thread::sleep_for(std::chrono::seconds(5));
        task();
    }
}

int main() {
    std::thread scheduler(scheduler_thread);
    scheduler.detach();

    while (true) {
        char c;
        std::cin >> c;
        if (c == 'a') {
            std::lock_guard<std::mutex> lock(task_mutex);
            task();
        } else {
            std::cout << "输入字符:" << c << std::endl;
        }
    }

    return 0;
}

方案2:IO多路复用(单线程)

  • 思路:利用select同时监听标准输入的可读事件和定时器超时事件。超时(5秒)时执行task(),有输入时处理用户输入,避免主循环阻塞在cin上。
  • 优势:无需线程,避免线程安全问题,适合单线程场景。
  • 代码示例(Linux):
#include <iostream>
#include <sys/select.h>
#include <unistd.h>

void task() {
    std::cout << "定时任务执行中..." << std::endl;
}

int main() {
    fd_set read_fds;
    struct timeval timeout;

    while (true) {
        FD_ZERO(&read_fds);
        FD_SET(0, &read_fds);

        timeout.tv_sec = 5;
        timeout.tv_usec = 0;

        int ret = select(1, &read_fds, nullptr, nullptr, &timeout);

        if (ret == -1) {
            perror("select error");
            break;
        } else if (ret == 0) {
            task();
        } else {
            char c;
            std::cin >> c;
            if (c == 'a') {
                task();
            } else {
                std::cout << "输入字符:" << c << std::endl;
            }
        }
    }

    return 0;
}

Windows平台需先初始化Winsock库,调用WSAStartup,也可使用WaitForSingleObject结合控制台输入句柄实现类似逻辑。

二、将调度器做成DLL插件

步骤1:定义DLL导出接口

先定义调度器对外接口,使用__declspec(dllexport)标记导出符号:

// scheduler_plugin.h
#ifdef SCHEDULER_PLUGIN_EXPORTS
#define SCHEDULER_API __declspec(dllexport)
#else
#define SCHEDULER_API __declspec(dllimport)
#endif

typedef void (*TaskCallback)();

SCHEDULER_API void InitScheduler(TaskCallback task);
SCHEDULER_API void StartScheduler();
SCHEDULER_API void StopScheduler();

步骤2:实现DLL内部逻辑

用线程方案实现调度器,管理线程生命周期:

// scheduler_plugin.cpp
#include "scheduler_plugin.h"
#include <thread>
#include <chrono>
#include <atomic>
#include <mutex>

std::atomic<bool> g_running(false);
std::thread g_scheduler_thread;
TaskCallback g_task = nullptr;
std::mutex g_task_mutex;

void scheduler_loop() {
    while (g_running) {
        std::this_thread::sleep_for(std::chrono::seconds(5));
        if (g_task != nullptr) {
            std::lock_guard<std::mutex> lock(g_task_mutex);
            g_task();
        }
    }
}

SCHEDULER_API void InitScheduler(TaskCallback task) {
    std::lock_guard<std::mutex> lock(g_task_mutex);
    g_task = task;
}

SCHEDULER_API void StartScheduler() {
    if (!g_running) {
        g_running = true;
        g_scheduler_thread = std::thread(scheduler_loop);
        g_scheduler_thread.detach();
    }
}

SCHEDULER_API void StopScheduler() {
    g_running = false;
}

步骤3:主程序加载DLL并使用

主程序通过LoadLibrary加载DLL,获取函数指针调用接口:

#include <iostream>
#include <windows.h>

typedef void (*InitSchedulerFunc)(void(*)());
typedef void (*StartSchedulerFunc)();
typedef void (*StopSchedulerFunc)();

void my_task() {
    std::cout << "自定义定时任务执行中..." << std::endl;
}

int main() {
    HMODULE hDll = LoadLibrary(L"scheduler_plugin.dll");
    if (hDll == nullptr) {
        std::cout << "加载DLL失败,错误码:" << GetLastError() << std::endl;
        return 1;
    }

    InitSchedulerFunc init_func = (InitSchedulerFunc)GetProcAddress(hDll, "InitScheduler");
    StartSchedulerFunc start_func = (StartSchedulerFunc)GetProcAddress(hDll, "StartScheduler");
    StopSchedulerFunc stop_func = (StopSchedulerFunc)GetProcAddress(hDll, "StopScheduler");

    if (!init_func || !start_func || !stop_func) {
        std::cout << "获取DLL函数指针失败" << std::endl;
        FreeLibrary(hDll);
        return 1;
    }

    init_func(my_task);
    start_func();

    while (true) {
        char c;
        std::cin >> c;
        if (c == 'a') {
            my_task();
        } else if (c == 'q') {
            stop_func();
            break;
        } else {
            std::cout << "输入字符:" << c << std::endl;
        }
    }

    FreeLibrary(hDll);
    return 0;
}

注意事项

  • 编译DLL时需定义SCHEDULER_PLUGIN_EXPORTS宏,确保导出符号正确生成。
  • 线程安全:任务函数涉及共享资源时,需在DLL或主程序中加锁保护。
  • 资源清理:主程序退出前必须调用StopScheduler,避免后台线程残留;释放DLL前确保所有DLL内线程已退出。
  • 跨平台:Linux下可使用共享对象(.so),导出符号用__attribute__((visibility("default"))),加载用dlopen、dlsym、dlclose。

内容的提问来源于stack exchange,提问作者Hrxun

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 08:23:24