我正在运行以下程序。它只是创建直接死亡的线程。
我发现在93到98(略有不同)成功调用之后,每次调用pthread_create()都会失败,错误11:资源暂时不可用。我认为我正在关闭线程所以它应该放弃它拥有的任何资源,但有些资源变得不可用。
程序的第一个参数允许我设置调用pthread_create()之间的间隔,但是使用不同的值进行测试,我已经知道间隔无关紧要(好吧,我会先得到错误):成功通话的次数将是相同的。
程序的第二个参数允许我在调用失败后设置一个休眠间隔,但是间隔的长度似乎没有任何区别。
我在这里打了哪个天花板?
编辑:在doSomething()中发现错误:将锁更改为解锁并且程序运行正常。问题仍然存在:未纠正错误导致哪些资源耗尽?
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <pthread.h>
#include <errno.h>
pthread_mutex_t doSomethingLock;
void milliSleep(unsigned int milliSeconds)
{
struct timespec ts;
ts.tv_sec = floorf(((float)milliSeconds / 1000));
ts.tv_nsec = ((((float)milliSeconds / 1000) - ts.tv_sec)) * 1000000000;
nanosleep(&ts, NULL);
}
void *doSomething(void *args)
{
pthread_detach(pthread_self());
pthread_mutex_lock(&doSomethingLock);
pthread_exit(NULL);
}
int main(int argc, char **argv)
{
pthread_t doSomethingThread;
pthread_mutexattr_t attr;
int threadsCreated = 0;
if (argc != 3)
{
fprintf(stderr, "usage: demo <interval between pthread_create() in ms> <time to wait after fail in ms>\n");
exit(1);
}
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP);
pthread_mutex_init(&doSomethingLock, &attr);
while (1)
{
pthread_mutex_lock(&doSomethingLock);
if (pthread_create(&doSomethingThread, NULL, doSomething, NULL) != 0)
{
fprintf(stderr, "%d pthread_create(): error %d, %m\n", threadsCreated, errno);
milliSleep(atoi(argv[2]));
}
else threadsCreated++;
milliSleep(atoi(argv[1]));
}
}
答案 0 :(得分:5)
如果您使用的是32位发行版,则可能达到了地址空间限制。我检查的最后一次,glibc将为每个创建的线程分配大约13MB的堆栈空间(这只是映射的大小,而不是分配的内存)。使用98个线程,您将超过3G可用的千兆字节地址空间。
您可以通过在错误发生后冻结您的流程(例如sleep(1000000)
或其他)并使用pmap
查看其地址空间来对此进行测试。
如果这是问题所在,请尝试在传递给pthread_attr_setstack()
的{{1}}上使用pthread_attr_t
设置较小的堆栈大小。您必须明确地判断堆栈需求,但即使是复杂的代码也只能在几千字节的堆栈中成功运行。
答案 1 :(得分:2)
您的程序“创建简单消失的线程”。它没有按照你的想法做到。
首先,pthread_mutex_unlock()
仅解锁已被同一线程锁定的pthread_mutex_t
。这就是互斥体的工作方式:它们只能通过锁定它们的同一个线程解锁。如果您想要信号量semaphore,use a semaphore。
您的示例代码会创建一个递归互斥锁,doSomething()
函数会尝试锁定该互斥锁。因为它由原始线程保持,所以它会阻塞(等待pthread_mutex_lock()
调用中的互斥锁变为空闲)。因为原始线程永远不会释放锁,所以只需在doSomethingLock
互斥锁之上堆积新线程。
关于互斥锁的递归只意味着一个线程可以多次锁定它;它必须解锁它的次数才能实际释放互斥锁。
如果您将pthread_mutex_lock()
中的doSomething()
更改为pthread_mutex_unlock()
,那么您正在尝试解锁该线程未持有的互斥锁。调用失败,然后线程立即死亡。
假设你修复了你的程序,接下来你会发现你不能创建超过一百个左右的线程(取决于你的系统和可用的RAM)。
Andy Ross很好地解释了这个原因:固定大小的堆栈(getrlimit(RLIMIT_STACK, (struct rlimit *)&info)
告诉你,除非你通过线程属性设置它,否则占用你的可用地址空间。
为进程提供的原始堆栈会自动调整大小,但对于所有其他线程,堆栈大小是固定的。默认情况下,它非常大;在我的系统上,8388608字节(8兆字节)。
我个人创建具有非常小的堆栈的线程,通常是65536字节,除非你的函数使用本地数组或大型结构,否则这些线程已经足够了,或者进行疯狂的深度递归:
#ifndef THREAD_STACK_SIZE
#define THREAD_STACK_SIZE 65536
#endif
pthread_attr_t attrs;
pthread_t thread[N];
int i, result;
/* Create a thread attribute for the desired stack size. */
pthread_attr_init(&attrs);
pthread_attr_setstacksize(&attrs, THREAD_STACK_SIZE);
/* Create any number of threads.
* The attributes are only a guide to pthread_create(),
* they are not "consumed" by the call. */
for (i = 0; i < N; i++) {
result = pthread_create(&thread[i], &attrs, some_func, (void *)i);
if (result) {
/* strerror(result) describes the error */
break;
}
}
/* You should destroy the attributes when you know
* you won't be creating any further threads anymore. */
pthread_attr_destroy(&attrs);
最小堆栈大小应该是PTHREAD_STACK_MIN
,并且应该是sysconf(_SC_PAGESIZE)
的倍数。目前PTHREAD_STACK_MIN == 16384
,但我建议使用更大的2的幂。 (在任何二进制架构上,页面大小总是 2的幂。)
它只是最小的,并且pthread库可以自由地使用它认为合适的任何更大的值,但实际上堆栈大小似乎是你设置的,加上一个固定值,取决于体系结构,内核,和pthread库版本。使用编译时常量几乎适用于所有情况,但如果您的应用程序足够复杂以获得配置文件,那么让用户在配置中覆盖编译时常量可能是个好主意文件。