【问题标题】:Is C++'s system() synchronized?C++ 的 system() 是否同步?
【发布时间】:2012-10-11 15:32:47
【问题描述】:

我正在编写一个小型实用程序,它应该使用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() 实例。

谢谢

【问题讨论】:

  • 请粘贴更多代码!

标签: c++ multithreading c++11 thread-safety


【解决方案1】:

POSIX 对system(3) 有这样的说法:

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

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

【讨论】:

  • 您能具体提出一个解决方案吗?
  • 这很重要,您需要做大量的簿记工作,并且以线程安全的方式进行操作可能会很棘手(有关有趣的问题,请参阅linuxprogrammingblog.com/…)。我不确定我是否知道如何正确编写它,所以我会使用一个库并希望它正确(参见stackoverflow.com/questions/1683665/where-is-boost-process)。
  • 我发布了我在下面创建的旧程序中使用的解决方案。它不在同一个进程中使用多个线程,而是通过fork创建一个新进程。
  • @Geoff_Montee:你应该看看我评论中的第一个链接。问题是从多个线程执行 fork/exec/wait 舞蹈,这恰好很快变得棘手。
  • 是的,这就是我加入免责声明的原因。我不建议在多线程程序中按原样使用它。如果可以使用多进程代替多线程,则它更多是一种选择。但我对 OP 的用例了解不多。
【解决方案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);

【讨论】:

  • 不错。仍然需要确保您没有使用包含 popen'd 进程的进程选择参数调用 waitpid/waitid 的其他任何东西,但这听起来是一个很好的解决方法。
  • 当然。但是,我想这次我会自我接受答案,因为它是解释问题的可能解决方案。
【解决方案3】:

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

正如@Mat 指出的那样,forkexecwait 并不是真正设计用于multi-threaded process

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

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;   
    }   
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-03-13
    • 2013-09-06
    • 1970-01-01
    • 2010-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多