为什么在Mac OS X上,当在线程中使用system()时,多线程C程序会被强制分配给单个CPU
Why is a multithreaded C program forced to a single CPU on Mac OS X when system() is used in a thread?
我在Linux和Mac OS X之间使用pthreads的程序的行为上遇到了一个奇怪的差异。
考虑以下可以使用"gcc-phread-o threadtest threadtest.c"编译的程序:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
static
void *worker(void *t)
{
int i = *(int *)t;
printf("Thread %d startedn", i);
system("sleep 1");
printf("Thread %d endsn", i);
return (void *) 0;
}
int main()
{
#define N_WORKERS 4
pthread_t workers[N_WORKERS];
int args[N_WORKERS];
int i;
for (i = 0; i < N_WORKERS; ++i)
{
args[i] = i;
pthread_create(&workers[i], NULL, worker, args + i);
}
for (i = 0; i < N_WORKERS; ++i)
{
pthread_join(workers[i], NULL);
}
return 0;
}
在4核Mac OS X计算机上运行生成的可执行文件会导致以下行为:
$ time ./threadtest
Thread 0 started
Thread 2 started
Thread 1 started
Thread 3 started
Thread 0 ends
Thread 1 ends
Thread 2 ends
Thread 3 ends
real 0m4.030s
user 0m0.006s
sys 0m0.008s
请注意,实际内核的数量可能甚至不相关,因为时间只是花在"sleep 1"shell命令中,而没有任何计算。同样明显的是,线程是并行启动的,因为"线程…启动"消息在程序启动后立即出现。
在Linux机器上运行相同的测试程序会得到我所期望的结果:
$ time ./threadtest
Thread 0 started
Thread 3 started
Thread 1 started
Thread 2 started
Thread 1 ends
Thread 2 ends
Thread 0 ends
Thread 3 ends
real 0m1.010s
user 0m0.008s
sys 0m0.013s
四个进程并行启动,每个进程睡眠一秒钟,大约需要一秒钟。
如果我将实际计算放入worker()函数并删除system()调用,我会在Mac OS X中看到预期的加速。
所以问题是,为什么在线程中使用system()调用可以有效地序列化Mac OS X上线程的执行,以及如何防止这种情况发生?
@BasileStarynkevitch和@null指出,Mac OS X的C库中的system()实现中的全局互斥可能是观察到的行为的原因@null提供了对系统()实现的潜在源文件的引用,其中包含以下操作:
#if __DARWIN_UNIX03
pthread_mutex_lock(&__systemfn_mutex);
#endif /* __DARWIN_UNIX03 */
#if __DARWIN_UNIX03
pthread_mutex_unlock(&__systemfn_mutex);
#endif /* __DARWIN_UNIX03 */
通过分解lldb中的system()函数,我验证了这些调用实际上存在于编译的代码中。
解决方案是将system()C库函数的使用替换为fork()/execve()/waitpid()系统调用的组合。修改原始示例中worker()函数的快速概念验证:
static
void *worker(void *t)
{
static const char shell[] = "/bin/sh";
static const char * const args[] = { shell, "-c", "sleep 1", NULL };
static const char * const env[] = { NULL };
pid_t pid;
int i = *(int *)t;
printf("Thread %d startedn", i);
pid = fork();
if (pid == 0)
{
execve(shell, (char **) args, (char **) env);
}
waitpid(pid, NULL, 0);
printf("Thread %d endsn", i);
return (void *) 0;
}
经过此修改,测试程序现在在Mac OS X上大约一秒钟即可执行。
相关文章:
- 试图创建一个多线程程序来查找0-100000000之间的总素数
- sigwait() 在多线程程序中不起作用
- 多线程程序中出现意外的内存泄漏
- 通过安装信号处理程序关闭多线程应用程序
- 多线程减慢程序速度:无错误共享,无互斥锁,无缓存未命中,无小工作量
- C++多线程程序:变量定义为类成员的隔离错误
- 多线程 gtkmm 应用程序最简单的示例
- 将数组作为多线程应用程序中函数的返回传递
- 修改多线程应用程序中的对象
- 在以读取为主的多线程程序中,可以使用原子来减少锁定吗
- 多线程程序中的分段故障和gdb回溯上的不完整信息
- 多线程程序卡在优化模式下,但在 -O0 中正常运行
- 多线程 C++11 应用程序中的同步
- 多线程Windows GUI应用程序中的死锁
- 在使用 std::cout 和多线程程序中如何避免数据竞争<iomanip>?
- 控制多线程程序中的输出流
- C++多线程应用程序将永远挂起
- 具有多线程应用程序的 Nanomsg 无阻塞双向套接字
- C++从多线程 CPU 程序迁移到 GPU
- C++应用程序多线程中的锁定方法