Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

如何优雅停止workflow创建的线程 #654

Closed
xophiix opened this issue Nov 24, 2021 · 4 comments
Closed

如何优雅停止workflow创建的线程 #654

xophiix opened this issue Nov 24, 2021 · 4 comments

Comments

@xophiix
Copy link

xophiix commented Nov 24, 2021

workflow当第一次触发一个task时会创建一堆线程,
我是在一个非workflow框架里用到workflow的WFHttpTask,实现批量向远端发起异步http请求,没有用到server。想在进程退出时能像server.stop那样停止workflow底层所有线程。停止进程时我已经将运行中task都dismiss,但偶尔还是会crash在某些线程

Program terminated with signal 5, Trace/breakpoint trap.
#0  0x00007f2ffd53fd71 in __nptl_death_event () from /lib64/libpthread.so.0
Missing separate debuginfos, use: debuginfo-install bzip2-libs-1.0.6-13.tl2.x86_64 elfutils-libelf-0.176-5.tl2.x86_64 elfutils-libs-0.176-5.tl2.x86_64 glibc-2.17-323.tl2.x86_64 keyutils-libs-1.5.8-3.tl2.x86_64 krb5-libs-1.15.1-50.tl2.x86_64 libattr-2.4.46-12.tl2.x86_64 libcap-2.22-10.tl2.x86_64 libcom_err-1.42.9-19.tl2.x86_64 libgcc-4.8.5-44.tl2.1.x86_64 libselinux-2.5-15.tl2.x86_64 libstdc++-4.8.5-44.tl2.1.x86_64 openssl-libs-1.0.2k-21.tl2.1.x86_64 pcre-8.32-17.tl2.x86_64 systemd-libs-219-78.tl2.3.x86_64 xz-libs-5.2.2-1.tl2.x86_64 zlib-1.2.7-19.tl2.x86_64
(gdb) bt
#0  0x00007f2ffd53fd71 in __nptl_death_event () from /lib64/libpthread.so.0
#1  0x00007f2ffd540ffc in start_thread () from /lib64/libpthread.so.0
#2  0x00007f2ffba039fd in clone () from /lib64/libc.so.6
@Barenboim
Copy link
Contributor

Barenboim commented Nov 24, 2021

首先需要解释一下,dismiss操作无法针对运行中的任务。dismiss的作用是直接删除一个创建完之后不想运行的task,已经启动的任务必须等callback回来。可以参考这个issue:#638
另外就是关闭线程的问题。如果不用什么特殊技巧,通信线程池是在第一次使用通信任务的时候创建,程序退出的时候销毁。我们不区分client和server,所以server stop并不会关闭通信线程。
如果你想提前关闭通信线程,也是有办法的。在所有通信任务结束之后调用:

#include "workflow/WFGlobal.h"
void my_close_scheduler()
{
    WFGlobal::get_scheduler()->deinit();
}

如果之后又想用通信任务的话,也是可以的,但需要先重新初始化一下:

int my_open_scheduler()
{
    const struct WFGlobalSettings *settings = WFGlobal::get_global_settings();
    return WFGlobal::get_scheduler()->init(settings->poller_threads, settings->handler_threads);
}

有一个小坑,因为程序退出会调用deinit。所以,如果你自己deinit过,程序退出之前最好重新init回来,可以调:WFGlobal::get_scheduler()->init(1, 1);

@xophiix
Copy link
Author

xophiix commented Nov 25, 2021

感谢大佬
如果task的callback还没有调用 就需要退出程序 如何处理比较合适

我想到的办法就是等待callback 延迟退出

@Barenboim
Copy link
Contributor

Barenboim commented Nov 25, 2021

这是个好问题。首先在网络任务上,程序退出会调用scheduler->deinit()。所以这个问题和网络任务callback还没有调用,能不能调scheduler->deinit()是一样的。和第一个问题可以结合起来。

我们的网络任务没有callback不能结束程序的原因是:在很多情况下,你看到的网络任务并不是一个原子任务,而是可能包含多个异步过程。以http为例,可能需要dns,302重定向,重试等。每个过程结束了,不会判断scheduler是否已经被deinit。但如果你确定一个任务是原子任务,那么程序退出并不会有任何问题,行为有严格定义。也就是说,以下程序是绝对安全的:

void callback(WFHttpTask *task)
{
    // 这里打印的结果大概率是2,WFT_STATE_ABORTED。
    printf("state = %d\n", task->get_state());
}
int main()
{
    WFHttpTask *task = WFTaskFactory::create_http_task("https://127.0.0.1/", 0, 0, callback);
    task->start();
    // 这里直接结束程序
    return 1;
}

所以你只要确定你的任务没有重定向,重试,使用IP或域名dns信息肯定能cache命里,那么可以安全的结束程序,也可以随时调用WFGlobal::get_scheduler()->deinit()。
另外,定时器任务也是一种原子任务,所以以下程序是安全的:

void callback(WFTimerTask *task)
{
    // 这里打印的结果肯定是2,WFT_STATE_ABORTED。
    printf("state = %d\n", task->get_state());
}
int main()
{
    WFTimerTask *task = WFTaskFactory::create_timer_task(1000000, callback);
    task->start();
    // 这里直接结束程序
    return 1;
}

@Barenboim
Copy link
Contributor

@xophiix

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants