如何在c++中创建异步函数

How do I make a function asynchronous in C++?

本文关键字:创建 异步 函数 c++      更新时间:2023-10-16

我想调用一个异步函数(当这个任务完成时我将给出一个回调)。

这可以用现代c++移植,甚至用旧的c++和一些boost来移植。boost和c++ 11都包含从线程获取异步值的复杂工具,但是如果你想要的只是回调,只需启动一个线程并调用它。

1998 c++/boost方法:

#include <iostream>
#include <string>
#include <boost/thread.hpp>
void callback(const std::string& data)
{
    std::cout << "Callback called because: " << data << 'n';
}
void task(int time)
{
    boost::this_thread::sleep(boost::posix_time::seconds(time));
    callback("async task done");
}
int main()
{
    boost::thread bt(task, 1);
    std::cout << "async task launchedn";
    boost::this_thread::sleep(boost::posix_time::seconds(5));
    std::cout << "main donen";
    bt.join();
}

2011 c++方法(使用gcc 4.5.2,需要这个#define)

#define _GLIBCXX_USE_NANOSLEEP
#include <iostream>
#include <string>
#include <thread>
void callback(const std::string& data)
{
    std::cout << "Callback called because: " << data << 'n';
}
void task(int time)
{
    std::this_thread::sleep_for(std::chrono::seconds(time));
    callback("async task done");
}
int main()
{
    std::thread bt(task, 1);
    std::cout << "async task launchedn";
    std::this_thread::sleep_for(std::chrono::seconds(5));
    std::cout << "main donen";
    bt.join();
}

从c++ 11开始,普通c++ 确实有线程的概念,但是异步调用函数最简洁的方法是使用c++ 11 async命令和future。这最终看起来很像你在pthreads中做同样事情的方式,但它100%可移植到所有操作系统和平台:

假设你的函数有一个返回值…int = MyFunc(int x, int y)

#include <future>

只做:

// This function is called asynchronously
std::future<int> EventualValue = std::async(std::launch::async, MyFunc, x, y); 

抓住吗?你怎么知道什么时候完成?(障碍)。

最终做的事:

int MyReturnValue = EventualValue.get(); // block until MyFunc is done

注意,以这种方式创建并行for循环很容易——只需创建一个期货数组。

在普通c++中不能。您需要使用特定于操作系统的机制,并且需要一个暂停执行的点,以允许操作系统执行回调。例如,对于Windows, QueueUserAPC -回调将被执行时,例如SleepExWaitForSingleObjectEx

较长的答案包括实现您自己的任务调度程序,并将您的"功能"包装成一个或多个任务。我不确定你是否想要冗长的答案。它当然不允许你调用某个东西,完全忘记它,然后在它完成时得到通知;然而,如果你有雄心壮志,它将允许你在某种程度上模拟协同程序,而无需超出标准c++。

简短的回答是这是不可能的。使用多个线程或多个进程。

如果你透露你正在开发的操作系统/平台,我可以给你更具体的信息。

要做到这一点需要两个步骤。

首先,将函数调用打包,以便稍后执行。

第二,调度。

调度取决于实现的其他方面。如果你知道"这个任务什么时候完成",那么你所需要的就是返回并检索"函数调用"并调用它。所以我不确定这一定是个大问题。

第一部分实际上是关于函数对象,甚至函数指针。后者是c的传统回调机制。

对于FO,您可能有:

class Callback
{
public:
  virtual void callMe() = 0;
};

你从这个推导出来,并按照你认为适合你的具体问题的方式实现它。那么异步事件队列只不过是回调的list<>:

std::list<Callback*> asyncQ; // Or shared_ptr or whatever.

我不确定我理解你想要什么,但如果它是如何利用回调:它通过定义一个函数指针来工作,像这样(未经测试):

// Define callback signature.
typedef void (*DoneCallback) (int reason, char *explanation);
// A method that takes a callback as argument.
void doSomeWorkWithCallback(DoneCallback done)
{
    ...
    if (done) {
       done(1, "Finished");
    }   
}
//////
// A callback
void myCallback(int reason, char *explanation)
{
    printf("Callback called with reason %d: %s", reason, explanation);
}
/////
// Put them together
doSomeWortkWithCallback(myCallback);

正如其他人所说,从技术上讲,在普通的c++中不能这样做。

然而,你可以创建一个管理器来处理你的任务,并进行时间切片或时间调度;对于每个函数调用,管理器使用计时器来测量该过程所花费的时间;如果进程花费的时间少于预定的时间,并且它认为它可以完成另一个调用,并且用完剩余的时间而不会超时,它可以再次调用它;如果函数确实超过了分配的时间,这意味着函数下一次更新运行的时间更少。因此,这将涉及创建一个稍微复杂的系统来为您处理它。

或者,如果您心中有一个特定的平台,您可以使用线程,或者创建另一个进程来处理工作。