从lib文件回调方法的最简单/有效方法

时间:2013-11-14 20:28:38

标签: c++ callback c++98

我目前正在从外部lib文件中调用一些方法。有没有办法让这些方法在应用程序完成后回调函数,因为这些方法可能在不同的线程中运行? 下图显示了我想要实现的目标

enter image description here

我想知道将消息发送回调用应用程序的最佳方法是什么?任何可能有帮助的提升组件?

1 个答案:

答案 0 :(得分:0)

编辑后

更新

目前尚不清楚你拥有什么。你是否控制了外部库启动的线程的线程入口点(这会让我感到惊讶)?

假设:

  • 库函数接受回调
  • 假设您不控制库函数的源,而不是后台线程中此库函数启动的线程函数
  • 您希望在原始主题上处理回调

你可以让回调存储在你从主线程定期检查的某种队列中的记录(当然没有繁忙的循环)。使用无锁队列,或使用例如同步访问队列。 std::mutex

更新以下是这样的排队版 Live on Coliru

#include <thread>
#include <vector>

//////////////////////////////////////////////////////////
// fake external library taking a callback
extern void library_function(int, void(*cb)(int,int));

//////////////////////////////////////////////////////////
// our client code
#include <iostream>
#include <mutex>

void callback_handler(int i, int input)
{
    static std::mutex mx;
    std::lock_guard<std::mutex> lk(mx);
    std::cout << "Callback #" << i << " from task for input " << input << "\n";
}

//////////////////////////////////////////////////////////
// callback queue
#include <deque>
#include <future>

namespace {
    using pending_callback = std::packaged_task<void()>;
    std::deque<pending_callback> callbacks;
    std::mutex callback_mutex;

    int process_pending_callbacks() {
        std::lock_guard<std::mutex> lk(callback_mutex);

        int processed = 0;
        while (!callbacks.empty()) {
            callbacks.front()();
            ++processed;
            callbacks.pop_front();
        }

        return processed;
    }

    void enqueue(pending_callback cb) {
        std::lock_guard<std::mutex> lk(callback_mutex);
        callbacks.push_back(std::move(cb));
    }
}

// this wrapper to "fake" a callback (instead queuing the real
// callback_handler)
void queue_callback(int i, int input)
{
    enqueue(pending_callback(std::bind(callback_handler, i, input)));
}

int main()
{
    // do something with delayed processing:
    library_function(3, queue_callback);
    library_function(5, queue_callback);

    // wait for completion, periodically checking for pending callbacks
    for (
        int still_pending = 3 + 5; 
        still_pending > 0; 
        std::this_thread::sleep_for(std::chrono::milliseconds(10))) // no busy wait
    {
        still_pending -= process_pending_callbacks();
    }
}

//////////////////////////////////////////////////////////
// somewhere, in another library:
void library_function(int some_input, void(*cb)(int,int))
{
    std::thread([=] {
        for (int i = 1; i <= some_input; ++i) {
            std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 5000)); // TODO abolish rand()
            cb(i, some_input);
        }
    }).detach();
}

典型输出:

Callback #1 from task for input 5
Callback #2 from task for input 5
Callback #1 from task for input 3
Callback #3 from task for input 5
Callback #2 from task for input 3
Callback #4 from task for input 5
Callback #5 from task for input 5
Callback #3 from task for input 3

请注意

  • 输出散布在两个工作线程
  • 但因为callbacks队列是FIFO,所以保留每个工作线程的回调序列

在您编辑问题之前,我就是这么想的: Live on Coliru

#include <thread>
#include <vector>

extern int library_function(bool);

static std::vector<std::thread> workers; // TODO implement a proper pool

void await_workers()
{
    for(auto& th: workers)
        if (th.joinable()) th.join();
}

template <typename F, typename C>
void do_with_continuation(F f, C continuation)
{
    workers.emplace_back([=] () mutable {
            auto result = f();
            continuation(result);
        });
}

#include <iostream>
#include <mutex>

void callback(int result)
{
    static std::mutex mx;
    std::lock_guard<std::mutex> lk(mx);
    std::cout << "Resulting value from callback " << result << "\n";
}

int main()
{
    // do something with delayed processing:
    do_with_continuation(std::bind(library_function, false), callback);
    do_with_continuation(std::bind(library_function, true),  callback);

    await_workers();
}

// somewhere, in another library:

#include <chrono>
int library_function(bool some_input)
{
    std::this_thread::sleep_for(std::chrono::seconds(some_input? 6 : 3));
    return some_input ? 42 : 0;
}

它将始终按顺序打印输出:

Resulting value from callback 0
Resulting value from callback 42

注意:

  • 确保您在此类回调中同步对共享状态的访问权限(在这种情况下,std::cout受锁定保护)
  • 你想创建一个线程池,而不是一个不断增长的(二手)线程向量