3

我正在编写一个小实用程序,它应该并行启动多个命令,system()并等待它们的结果用于记录目的。然而,即使我system()在不​​同的线程上调用,通过查看我的活动监视器,我一次只能看到每个命令的一个实例。看起来系统在互斥锁上内部同步,每次只允许执行一次,但这看起来是一个巨大的限制,有人可以确认这种行为吗?您对如何解决它有任何想法吗?

通过查看线程执行流程进行更新,看起来它们有效地在互斥锁上同步。system()有没有不这样做的替代方案?

线程

我应该提到我在 Mac OS 10.7.5 上使用 C++11(w/clang 和 libc++)。

更新代码为:

void Batch::run()
{
    done.clear();
    generator->resetGeneration();

    while(generator->hasMoreParameters())
    {
        // Lock for accessing active
        unique_lock<mutex> lock(q_mutex, adopt_lock);

        // If we've less experiments than threads
        if (active.size() < threads)
        {
            Configuration conf = generator->generateParameters();
            Experiment e(executable, conf);

            thread t(&Experiment::run, e, reference_wrapper<Batch>(*this));
            thread::id id = t.get_id();
            active.insert(id);
            t.detach();
        }

        // Condition variable
        q_control.wait(lock, [this] { return active.size() < threads; } );

    }
}

void Batch::experimentFinished(std::thread::id pos)
{
    unique_lock<mutex> lock(q_mutex, adopt_lock);
    active.erase(pos);
    lock.unlock();
    q_control.notify_all();
}

void Experiment::run(Batch& caller)
{    
    // Generate run command
    stringstream run_command;
    run_command << executable + " ";
    ParameterExpression::printCommandLine(run_command, config);

    if (system(run_command.str().c_str()))
        stats["success"] = "true";
    else
        stats["success"] = "false";

    caller.experimentFinished(this_thread::get_id());
}

需要明确一点:线程的生成和处理工作正常,可以完成它需要做的事情,但看起来你一次只能system()运行一个实例。

谢谢

4

3 回答 3

3

POSIX 有这样的说法system(3)

在一个进程中的多个线程中使用 system() 函数,或者当进程中的多个线程正在操作 SIGCHLD 信号时,可能会产生意想不到的结果。

由于在执行期间必须阻止 SIGCHLD 的方式,system并发运行调用实际上不起作用。如果您希望多个线程运行外部任务,则需要编写更多代码(//fork自己处理)。execwait

于 2012-10-11T15:42:53.030 回答
2

对于后来来的人来说,popen成功了,因为它内部没有保留互斥锁。使其工作的代码是

FILE* proc;
char buff[1024];

// Keep track of the success or insuccess of execution
if (!(proc = popen(run_command.str().c_str(), "r")))
    stats["success"] = "false";
else
    stats["success"] = "true";

// Exhaust output
while(fgets(buff, sizeof(buff), proc) != nullptr);

pclose(proc);
于 2012-10-12T07:49:29.510 回答
1

如果这有帮助,我不久前用 C++ 编写了一些 fork/exec/wait 代码。它将输出捕获到一个std::string.

正如@Mat 指出的那样,fork,execwait并不是真正设计用于多线程进程的

因此,如果多进程可以替代应用程序中的多线程,这将更加有用。

bool Utility::execAndRedirect(std::string command, std::vector<std::string> args,     std::string& output, int& status)
{
    int error;
    int pipefd[2];
    int localStatus;

    if (pipe(pipefd) == -1)
    {
        error = errno;
        cerr << "Executing command '" << command << "' failed: " << strerror(error) << endl;
        return false;
    }

    pid_t pid = fork();

    if (pid == 0)
    {       
        char** argsC;

        argsC = new char*[args.size() + 2];

        argsC[0] = new char[command.size() + 1];

        strncpy(argsC[0], command.c_str(), command.size());

        argsC[0][command.size()] = '\0';

        for (size_t count = 0; count < args.size(); count++)
        {
            argsC[count + 1] = new char[args[count].size() + 1];

            strncpy(argsC[count + 1], args[count].c_str(), args[count].size());

            argsC[count + 1][args[count].size()] = '\0';            
        }

        argsC[args.size() + 1] = NULL;

        close(pipefd[0]); 

        if (dup2(pipefd[1], STDOUT_FILENO) == -1)
        {
            error = errno;
            cerr << "Executing command '" << command << "' failed: " << strerror(error) << endl;
            exit(1);        
        }       

        if (dup2(pipefd[1], STDERR_FILENO) == -1)
         {
            error = errno;
            cerr << "Executing command '" << command << "' failed: " << strerror(error) << endl;
            exit(1);        
        }       

        close(pipefd[1]);

        if (execvp(command.c_str(), argsC) == -1)
        {
            error = errno;
            cerr << "Executing command '" << command << "' failed: " << strerror(error) << endl;
            exit(1);
        }
    }

    else if (pid > 0)
    {
        size_t BUFFER_SIZE = 1024;
        char buffer[BUFFER_SIZE + 1];

        close(pipefd[1]);

        ostringstream oss;

        ssize_t num_b;

        while ((num_b = read(pipefd[0], buffer, BUFFER_SIZE)) != 0)
        {
            buffer[num_b] = '\0';

            oss << buffer;
        }

        output = oss.str();

        waitpid(pid, &localStatus, 0);

        close(pipefd[0]);
    }

    else
    {
        error = errno;
        cerr << "Executing command '" << command << "' failed: " << strerror(error) << endl;
        return false;   
    }

    if(WIFEXITED(localStatus))
    {
        status = WEXITSTATUS(localStatus);

        //DateTime current = DateTime::now(); //this is a custom class

        if(status == 0)
        {
            return true;
        }

        else
        {
             return false;
        }
    }

    else
    {
        error = errno;
        cerr << "Executing command '" << command << "' failed: child didn't terminate normally" << endl;
        return false;   
    }   
}
于 2012-10-11T19:22:53.057 回答