我目前正在从外部 lib 文件中调用一些方法。这些方法完成后是否有办法在我的应用程序中回调函数,因为这些方法可能在单独的线程中运行?下图显示了我想要实现的目标
我想知道将消息发送回调用应用程序的最佳方式是什么?任何可能有帮助的增强组件?
修改后更新:
目前还不清楚你有什么。您是否控制由外部库启动的线程的线程入口点(这让我感到惊讶)?
假设:
您可以让回调将记录存储在您定期从主线程检查的某种队列中(当然,没有繁忙的循环)。使用无锁队列,或使用例如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
受锁保护)