1

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

在此处输入图像描述

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

4

1 回答 1

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队列是先进先出的,所以每个工作线程的回调序列被保留了

这就是我在编辑问题之前的想法: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受锁保护)
  • 你想创建一个线程池,而不是一个不断增长的(使用过的)线程向量
于 2013-11-14T20:52:45.140 回答