3

私はいくつかのコマンドを並行して起動しsystem()、ログ記録のためにそれらの結果を待つ小さなユーティリティを書いています。ただし、system()別のスレッドで呼び出しているにもかかわらず、Activity Monitor を見ると、一度に各コマンドのインスタンスが 1 つしか表示されません。システムはミューテックスで内部的に同期されているようで、一度に 1 つの実行しか許可されていませんが、これは大きな制限のように見えます。誰かがこの動作を確認できますか? それを解決する方法についてのアイデアはありますか?

スレッドの実行フローを見て更新すると、ミューテックスで効果的に同期しているように見えます。system()それを行わない代替手段はありますか?

スレッド

Mac OS 10.7.5でC++ 11(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()実行しますが、一度に実行できるインスタンスは 1 つだけのようです。

ありがとう

4

3 に答える 3

3

POSIXには、これについて次のように書かれていsystem(3)ます:

プロセス内の複数のスレッドで system() 関数を使用するか、プロセス内の複数のスレッドによって SIGCHLD シグナルが操作されているときに、予期しない結果が生じる場合があります。

実行中に SIGCHLD をブロックする必要があるため、system呼び出しを同時に実行しても実際には機能しません。複数のスレッドで外部タスクを実行したい場合は、もう少しコードを書く必要があります ( fork/ exec/wait自分で処理します)。

于 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 が指摘しているように、forkexec、およびは、実際にはマルチスレッド プロセスwaitで使用するようには設計されていません。

そのため、アプリケーションでマルチプロセスがマルチスレッドの代わりになる場合、これはより便利です。

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 に答える