为什么_beginthreadex失败了ERROR_INVALID_ACCESS?

时间:2014-02-07 01:26:06

标签: c++ windows multithreading qt

我将一堆QWebView渲染到一个小部件上。有一点我开始收到错误QThread::start: Failed to create thread (The access code is invalid.)。看一下Qt源代码,看来_beginthreadex返回一个空句柄而errno是ERROR_INVALID_ACCESS,但我不明白为什么。

以下是打印错误时的回溯:

0   qErrnoWarning                           qglobal.cpp    2451    0x69ccdd3c  
1   QThread::start                          qthread_win.cpp 469 0x69cd5831  
2   QThreadPoolPrivate::tryStart            qthreadpool.cpp 203 0x69ccc3f5  
3   QThreadPool::start                      qthreadpool.cpp 474 0x69cccdf4  
4   QHostInfoLookupManager::work            qhostinfo.cpp   633 0x6cb9b071  
5   QHostInfoLookupManager::scheduleLookup  qhostinfo.cpp   652 0x6cb9b143  
6   QHostInfo::lookupHost                   qhostinfo.cpp   202 0x6cb9a220  
7   qt_qhostinfo_lookup                     qhostinfo.cpp   722 0x6cb9b4b6  
8   QAbstractSocket::connectToHostImplementation    qabstractsocket.cpp 1427    0x6cbb17f5  
9   QAbstractSocket::qt_static_metacall     moc_qabstractsocket.cpp 166 0x6cbb4925  
10  QMetaMethod::invoke                     qmetaobject.cpp 1664    0x69dc784f  
11  QMetaObject::invokeMethod               qmetaobject.cpp 1179    0x69dc6d6b  
12  QMetaObject::invokeMethod               qobjectdefs.h   418 0x6cd361dd  
13  QAbstractSocket::connectToHost          qabstractsocket.cpp 1342    0x6cbb13b3  
14  QSslSocket::connectToHostImplementation qsslsocket.cpp  1744    0x6cbc7340  
15  QSslSocket::qt_static_metacall          moc_qsslsocket.cpp  91  0x6cbc93cf  
16  QMetaMethod::invoke                     qmetaobject.cpp 1664    0x69dc784f  
17  QMetaObject::invokeMethod               qmetaobject.cpp 1179    0x69dc6d6b  
18  QMetaObject::invokeMethod               qobjectdefs.h   418 0x6cd361dd  
19  QAbstractSocket::connectToHost          qabstractsocket.cpp 1342    0x6cbb13b3  
20  QSslSocket::connectToHostEncrypted      qsslsocket.cpp  422 0x6cbc55e1  
21  QHttpNetworkConnectionChannel::ensureConnection qhttpnetworkconnectionchannel.cpp   607 0x6cb6191f  
22  QHttpNetworkConnectionPrivate::_q_startNextRequest  qhttpnetworkconnection.cpp  862 0x6cb5e92c  
23  QHttpNetworkConnectionPrivate::queueRequest qhttpnetworkconnection.cpp  501 0x6cb5c57d  
24  QHttpNetworkConnection::sendRequest     qhttpnetworkconnection.cpp  931 0x6cb5edf2  
25  QHttpThreadDelegate::startRequest       qhttpthreaddelegate.cpp 291 0x6cb8912a  
26  QHttpThreadDelegate::qt_static_metacall moc_qhttpthreaddelegate_p.cpp   113 0x6cbd147c  
27  QMetaCallEvent::placeMetaCall           qobject.cpp 525 0x69dcf91c  
28  QObject::event                          qobject.cpp 1195    0x69dd08db  
29  QApplicationPrivate::notify_helper      qapplication.cpp    4551    0x2582f44   
30  QApplication::notify                    qapplication.cpp    3933    0x25808b7   
31  QCoreApplication::notifyInternal        qcoreapplication.cpp    915 0x69dc0dc6  
32  QCoreApplication::sendEvent             qcoreapplication.h  231 0x69e35185  
33  QCoreApplicationPrivate::sendPostedEvents   qcoreapplication.cpp    1539    0x69dc1d2a  
34  qt_internal_proc                        qeventdispatcher_win.cpp    496 0x69de2590  
35  USER32!OffsetRect                       C:\Windows\syswow64\user32.dll  0   0x74cc62fa  
36  ??      0   0x152404    
37  ??      0   0x401   
38  ??      0       

通话时的代码如下:

d->handle = (Qt::HANDLE) _beginthreadex(NULL, d->stackSize, QThreadPrivate::start, //d->stackSize is 0
                                            this, CREATE_SUSPENDED, &(d->id));

if (!d->handle) {
    qErrnoWarning(errno, "QThread::start: Failed to create thread");
    d->running = false;
    d->finished = true;
    return;
}

为什么会发生这种情况,我该如何解决?

编辑:另外值得注意的是,在这一点上确实有500个线程。

1 个答案:

答案 0 :(得分:4)

在创建500个线程后,很有可能在进程中(对于线程堆栈)耗尽了空闲地址空间。在32位Windows上,默认情况下进程只获得2GB的地址空间(地址空间的上半部分是为内核保留的)。 500个1MB线程堆栈(默认大小,Qt可能更高或更低)加上您的进程所做的所有其他分配可以很容易地使用它。

有关详情,请参阅this Old New Thing article

可能的修复:

  1. 如果您知道您的QThreads不需要非常大的堆栈,您可以在启动线程之前调用QThread :: setStackSize()来设置更小的大小。
  2. 考虑使用线程池和/或只是减少启动的并发线程数。您不可能拥有足够的CPU内核来使500多个线程生产效率。
  3. 使用Windows / 3GB开关,使应用程序LARGE ADDRESS AWARE获得3GB的用户模式地址空间。
  4. 转到64位(用于63位用户模式地址空间)。