我正在编写一个小实用程序,它应该使用system()
并行启动多个命令并等待其结果用于记录目的。但是,即使我在不同的线程上调用system()
,通过查看我的Activity Monitor,我一次只能看到每个命令的一个实例。看起来系统在互斥锁上内部同步,每次只允许一次执行,但这看起来像一个巨大的限制,有人可以确认这种行为吗?你对如何解决它有什么想法吗?
更新,看起来它们正在有效地在互斥锁上进行同步。是否有替代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()
个实例。
由于
答案 0 :(得分:3)
POSIX对此有system(3)
:
在进程中的多个线程中使用system()函数,或者当进程中的多个线程操纵SIGCHLD信号时,可能会产生意外结果。
由于在执行期间必须阻止SIGCHLD的方式,同时运行system
调用并不真正起作用。如果您希望多个线程运行外部任务,则需要编写更多代码(自己处理fork
/ exec
/ wait
)。
答案 1 :(得分: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);
答案 2 :(得分:1)
如果这有帮助,我会在C ++中写一些fork / exec / wait代码。它将输出捕获到std::string
。
正如@Mat指出的那样,fork
,exec
和wait
并非真正设计为在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;
}
}