C++的异步函数调用

6
我需要提示如何在C/C++中实现异步函数调用(或Windows和/或Linux的框架/API调用名称)。
使用情况如下:父线程调用一个函数。该函数创建一个子线程并返回,因此调用是非阻塞的,父线程可以继续执行一些工作。
例如pthread_join无法获得结果,因此结果必须存储在堆中,并且必须通知父进程。我想要的是在子线程完成工作后在父线程中执行的回调函数。
令人惊讶的是,我在Google上找不到任何示例。
感谢您的帮助。

只是为了让我们了解你的问题所基于的语言/框架是什么。你似乎有非常具体的想法。如果你能提供一个参考,可能会有人知道C++的等效框架。但是如下所答,基本的构建块将是boost::threads,尽管与你想要的相比,这非常低级。 - Martin York
12
选择哪个,C 还是 C++?!下定决心吧! - Edward Strange
8个回答

8

7
与使用不同的框架相比,考虑到您在问题中提到了pthread_join(),您仍然可以通过POSIX C实现所需的效果。为此,您可以使用信号来确认并行任务完成的线程。在这种方法中,您需要为用户定义的信号(例如SIGUSR1)安装一个信号处理程序,创建一组具有不同任务的工作线程,并让它们在完成时向父线程发出信号。
下面的程序说明了这个尝试。在这个例子中,我使用SIGUSR1来通知父线程某些处理已经完成。父线程一直忙于执行一些I/O操作,直到被子线程中断。请注意,为了清晰起见,没有放置任何形式的错误处理代码。
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

/* Variable to hold the value of some calculation. */
int value = 0;

/* Variable used by the parent thread to inform itself of task completion. */
volatile sig_atomic_t value_received = 0;

/* Signal handler to handle the SIGUSR1 signal (the signal used to acknowledge the parent of task completion). */
void handler(int signum) {
    value_received = 1;
}

/* Working thread routine. First parameter is a pthread_t (cast to void*) identifying the parent. */
void *thread(void *parent) {
    /* Do something lengthy here, such as a long calculation. */
    value = 1;
    sleep(5); /* Simulate lengthy operation. */

    /* After processing, inform the parent thread that we have ended. */
    pthread_kill((pthread_t)parent, SIGUSR1);
    return NULL;
}

int main(void) {
    struct sigaction action;
    pthread_t child;

    /* Install signal handler to receive the child thread notification. */
    action.sa_handler = handler;
    sigemptyset(&action.sa_mask);
    action.sa_flags = 0;
    sigaction(SIGUSR1, &action, NULL);

    /* Create child thread that will perform some task. */
    pthread_create(&child, NULL, thread, (void*)pthread_self());

    /* Detach thread from execution. No need to join the thread later. */
    pthread_detach(child);

    /* Do some other processing while the ongoing task is running in parallel. */
    while (!value_received) {
        char buffer[0x100];

        /* Echo some input until something happens. */
        if (!fgets(buffer, sizeof buffer, stdin))
            break;
        printf("You typed: %s", buffer);
    }

    /* Something happened (signal received or EOF in stdin). In the latter, just sleep a little while. */
    if (feof(stdin))
        while (!value_received)
            sleep(1);

    /* At this point, child thread has already ended the execution. */
    printf("Value received: %i\n", value);
    return EXIT_SUCCESS;
}

这个示例使用LinuxThreads实现的信号,与POSIX定义的相当不同。如果你关心可移植性或兼容性,上述解决方案应进一步修改。


1
这是标准的,而且它是C语言,但它不是标准C语言。(sigaction、pthreads等是POSIX或SUS标准的一部分,而不是ANSI C标准的一部分) - Ben Voigt
@BenVoigt 您是正确的。然而,我倾向于认为大多数C和C++程序员可能倾向于使用Linux,并且默认情况下,大多数主要的Linux发行版都附带有POSIX。如果我错了,请纠正我。 - enthusiasticgeek
@enthusiasticgeek:你错了。Linux在C和C++市场份额相对较小。特别是对于C,大多数平台都太小而无法运行Linux。对于桌面应用程序,大多数程序员希望它也能在Windows上运行,即使他们使用Linux进行开发。但即使在开发者社区中,Windows的市场份额也比Linux更高(当然,差距比用户之间的差距小得多)。 - Ben Voigt
但是,即使您的评论是正确的,错误地编写“标准C”也没有任何优势。 - Ben Voigt
@BenVoigt 我明白了。可能是因为我在Linux上专门使用C++(嵌入式和服务器),而且我的办公室里大多数人也是这样。但是我们的前端都是Windows和Java。我之前也没有注意到他写的“标准C”(强调-标准)评论。 - enthusiasticgeek


3

Boost.Thread

我认为这个很好地完成了工作。将函数调用作为新线程启动,不要打扰加入它。我相信它完成时会自己处理的。

您可以设置子进程在使用Boost.Signals时向父进程发送信号。该信号将链接到父回调函数。


我已经查看了boost-signals,但似乎并不是我想要的。虽然我还没有试过,但看起来“slot”函数将在当前(在我的情况下是子线程)线程的上下文中执行。如果我错了,请纠正我。 - iddqd
误解了问题。Boost.Thread 才是你想要的。正在编辑我的回答。 - Jonathan Sternberg

1

使用boost::thread作为标准的跨平台解决方案。如果需要更复杂的东西,可以考虑使用英特尔的Thread Building Blocks。


我查看了文档,似乎boost没有提供这样的实现。无法为线程或future对象提供“回调”函数。虽然有set_wait_callback,但仅在future对象未准备好时才会调用。 - iddqd

0

你需要建立某种基础设施来通知调用者工作已完成。这可以使用观察者模式类型来完成。也可以使用消息类型基础设施。

真正的问题是在等待期间,你的父级要做什么。你想让它轮询结果吗?还是接收通知?


一个父线程可以处理GUI的消息循环。我想得到通知 - 给函数一个指向缓冲区的指针来存储结果总是可行的,由于线程在同一进程中,因此结果可以在“回调”函数中轻松读取。 - iddqd

0

使用Qt框架和Thread类或Qt Concurrency框架。

连接到QThread :: finished()或QFutureWatcher :: finished()信号以在完成工作时收到通知。


虽然我通常会支持Qt,但在这里我会选择TBB或类似的东西。 - Frank Osterfeld

0

我在你回复Starkey的时候看到主线程是UI线程。

我使用过几种不同的GUI工具包,它们都允许您从不同的线程发布消息。因此,当工作线程完成时,请让它发布GUI消息,处理程序将在主线程中运行。

在Windows中,使用PostMessage

在Linux中,使用XtAppAddSignalXtNoticeSignal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接