每个线程堆栈的默认大小是在您的测试中人为地施加限制。虽然赋予进程(初始线程)的默认堆栈根据需要动态增长,但其他线程的堆栈大小是固定的。默认大小通常非常大,例如 2 MB,以确保每个线程的堆栈足够大,即使是病态的情况(深度递归等)。
在大多数情况下,线程工作者需要很少的堆栈。我发现在我使用的所有架构上,每个线程堆栈 64k(65536 字节)就足够了,只要我不使用深度递归算法或大型局部变量(结构或数组)。
要显式指定每个线程的堆栈大小,请将您的修改main()
为以下内容:
#define MAXTHREADS 1000000
#define THREADSTACK 65536
int main(int argc, char *argv[])
{
pthread_t pid[MAXTHREADS];
pthread_attr_t attrs;
int err, i;
int cnt = 0;
pthread_attr_init(&attrs);
pthread_attr_setstacksize(&attrs, THREADSTACK);
pthread_mutex_init(&mutex_, NULL);
for (cnt = 0; cnt < MAXTHREADS; cnt++) {
err = pthread_create(&pid[cnt], &attrs, (void*)inc_thread_nr, NULL);
if (err != 0)
break;
}
pthread_attr_destroy(&attrs);
for (i = 0; i < cnt; i++)
pthread_join(pid[i], NULL);
pthread_mutex_destroy(&mutex_);
printf("Maximum number of threads per process is %d (%d)\n", cnt, thread_nr);
}
Note that attrs
is not consumed by the pthread_create()
call. Think of the thread attributes more like a template on how pthread_create()
should create the threads; they are not attributes given to the thread. This trips up many aspiring pthreads programmers, so it's one of those things you'd better get right from the get go.
As to the stack size itself, it must be at least PTHREAD_STACK_MIN
(16384 in Linux, I believe) and divisible by sysconf(_SC_PAGESIZE)
. Since page size is a power of two on all architectures, using a large enough power of two should always work.
Also, I added a fix in there, too. You only try to join a nonexistent thread (the one that the loop tried to create, but failed), but you need to join all of them (to make sure they're all done their job).
Further recommended fixes:
Instead of using a sleep, use a condition variable. Have each thread wait (pthread_cond_wait()
) on the condition variable (while holding the mutex), then release the mutex and exit. That way your main function only needs to broadcast (pthread_cond_broadcast()
) on the condition variable to tell all threads they can now exit, then it can join each one, and you can be sure that that number of threads were really concurrently running. As your code stands now, some threads may have enough time to wake up from the sleep and exit.