多进程等待的跨平台实现

最近在xmake中,用lua的协程实现了多任务编译,效果还是不错的,不过后来发现一个问题:

如果所有编译进程都在处理编译,没有退出的时候,xmake的lua主进程会不断地在这些任务间,不停的切换轮询进程的状态,但是有没有机会执行其他任务,导致cpu过高,抢占了编译进程的cpu时间。。

那如果在等不到完成的进程时候,加入sleep等待呢,又会导致编译速度变慢,没法合理利用cpu。。

因此,为了解决这个问题,我打算扩展下lua的接口,实现了一个跨平台的多进程等待接口: process.waitlist 实现多个未完成进程的同时等待,让出xmake主进程的cpu时间,给其他编译进程充分利用

xmake中的lua代码如下:

    -- wait processes    local tasks_finished = {}    local procs_count = # procs    if procs_count > 0 then        -- wait them        local procinfos = process.waitlist(procs, ifelse(procs_count 

在os.exec运行进程的接口实现中,如果当前进程没有立即退出,就通过协程切换出去,知道上面的多进程等待,获取到实际的退出进程后,直接定向切换到退出进程的os.exec中,继续完成后续操作,这样就不会有冗余切换问题:

-- execute shell
function os.exec(cmd, outfile, errfile)

-- open commandlocal ok = -1local proc = process.open(cmd, outfile, errfile)if proc ~= nil then    -- wait process    local waitok = -1    local status = -1     if coroutine.running() then        -- save the current directory        local curdir = os.curdir()        -- wait it        repeat            -- poll it            waitok, status = process.wait(proc, 0)            if waitok == 0 then                -- 外面的多进程等待到实际的状态值后,直接进行处理                waitok, status = coroutine.yield(proc)            end        until waitok ~= 0        -- resume the current directory        os.cd(curdir)    else        waitok, status = process.wait(proc, -1)    end    -- get status    if waitok > 0 then        ok = status    end    -- close process    process.close(proc)end-- ok?return ok

end
lua的上层调用有了,那怎么去实现这个跨平台的多进程等待呢?

在windows上我们能想到就是WaitForMultipleObjects这个接口了,我把它封装到了tbox里面具体实现如下:

tb_long_t tb_process_waitlist(tb_process_ref_t const* processes, tb_process_waitinfo_ref_t infolist, tb_size_t infomaxn, tb_long_t timeout)
{
// check
tb_assert_and_check_return_val(processes && infolist && infomaxn, -1);

// make the process listtb_size_t               procsize = 0;HANDLE                  proclist[256] = {0};tb_process_t const     pprocess = (tb_process_t const )processes;for (; *pprocess && procsize pi.hProcess;tb_assertf(procsize WaitForMultipleObjects(procsize, proclist, FALSE, timeout GetExitCodeProcess(process->pi.hProcess, &exitcode)? (tb_long_t)exitcode : -1;          infosize++;        // close thread handle        tb_kernel32()->CloseHandle(process->pi.hThread);        process->pi.hThread = INVALID_HANDLE_VALUE;        // close process        tb_kernel32()->CloseHandle(process->pi.hProcess);        process->pi.hProcess = INVALID_HANDLE_VALUE;        // next index        index++;        while (index WaitForMultipleObjects(procsize - index, proclist + index, FALSE, 0);            switch (result)            {            case WAIT_TIMEOUT:                // no more, exit loop                index = procsize;                break;            case WAIT_FAILED:                return -1;            default:                {                    // the process index                    index += result - WAIT_OBJECT_0;                    // the process                    process = (tb_process_t*)processes[index];                    tb_assert_and_check_return_val(process, -1);                    // save process info                    infolist[infosize].index    = index;                    infolist[infosize].process  = (tb_process_ref_t)process;                    infolist[infosize].status   = tb_kernel32()->GetExitCodeProcess(process->pi.hProcess, &exitcode)? (tb_long_t)exitcode : -1;                      infosize++;                    // close thread handle                    tb_kernel32()->CloseHandle(process->pi.hThread);                    process->pi.hThread = INVALID_HANDLE_VALUE;                    // close process                    tb_kernel32()->CloseHandle(process->pi.hProcess);                    process->pi.hProcess = INVALID_HANDLE_VALUE;                    // next index                    index++;                }                break;            }        }    }    break;}// ok?return infosize;

}
如果在linux以及其他posix系统上,可以使用wait或者waitpid 接口,其实wait也就是相当于调用了 waitpid(-1, &status, ..),所以我这里就直接使用waitpid来实现了。。

它跟windows的WaitForMultipleObjects有些不同,不能传递指定需要等待哪些进程句柄,想要等待多个进程,只能传递-1,表示等待所有子进程

不过我们在封装接口的时候,可以还是传入多个要等待的子进程列表,如果获取到的子进程不在这个列表里面,就直接忽略掉,有的话就返回出来,这样的话,行为上就跟windows的差不多了。。

tb_long_t tb_process_waitlist(tb_process_ref_t const* processes, tb_process_waitinfo_ref_t infolist, tb_size_t infomaxn, tb_long_t timeout)
{
// check
tb_assert_and_check_return_val(processes && infolist && infomaxn, -1);

// donetb_long_t infosize = 0;tb_hong_t time = tb_mclock();do{    // wait it    tb_int_t    status = -1;    tb_long_t   result = waitpid(-1, &status, timeout pid != result; pprocess++) ;        // found?        if (*pprocess)        {            // save process info            infolist[infosize].index = (tb_process_ref_t const*)pprocess - processes;            infolist[infosize].process = (tb_process_ref_t)*pprocess;            infolist[infosize].status = WIFEXITED(status)? WEXITSTATUS(status) : -1;            infosize++;            // attempt to wait other processes            while (infosize pid != result; pprocess++) ;                // found?                if (*pprocess)                {                    // save process info                    infolist[infosize].index = (tb_process_ref_t const*)pprocess - processes;                    infolist[infosize].process = (tb_process_ref_t)*pprocess;                    infolist[infosize].status = WIFEXITED(status)? WEXITSTATUS(status) : -1;                    infosize++;                }                else break;            }            // end            break;        }    }    // wait some time    if (timeout > 0) tb_msleep(tb_min(timeout, 60));} while (timeout > 0 && tb_mclock() - time 

最后贴下这个跨平台接口的是如何使用的,这里给了一个比较完整的demo

// init processestb_size_t        count1 = 0;tb_process_ref_t processes1[5] = {0};tb_process_ref_t processes2[5] = {0};for (; count1  0)    {        tb_size_t i = 0;        for (i = 0; i 
  1. XMake项目主页

  2. XMake项目详情

  3. XMake项目源码

  4. XMake项目文档

关键字:lua, c, 进程池, 协程


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部