注意:我已经为此添加了C ++标记,因为a)代码是C ++,b)使用C ++的人可能已经使用了IO完成端口。所以请不要喊。
<小时/> 我正在使用IO完成端口,最终完全理解(并经过测试,证明) - 在RbMm的帮助下 -
NumberOfConcurrentThreads
中CreateIoCompletionPort()
参数的含义。
我有以下小程序,它创建了10个线程,所有线程都在完成端口上等待。我告诉我的完成端口只允许4个线程同时运行(我有4个CPU)。然后我将8个数据包排入端口。如果我的线程函数使ID>&gt;的队列出列,则它会输出一条消息。 4;为了输出这条消息,我必须至少停止当前运行的四个线程中的一个,当我输入&#39; 1&#39;在控制台。
现在这是相当简单的代码。然而,我有一个很大的担忧,那就是如果处理完成数据包的所有线程都陷入困境,那就意味着没有更多的数据包可以出列和处理。 这就是我用无限循环模拟 - 事实上,在我输入&#39; 1&#39;之前不再有数据包出列。在控制台突出了这个潜在的问题!
更好的解决方案不是让我的四个线程使数据包出队(或者像CPU一样多的线程),然后当一个出队时,将该数据包的处理从一个单独的池中移植到工作线程 < / em>,从而消除了IOCP中所有线程陷入困境的风险,从而不再有数据包出队了?
我问这是所有我看到的IO完成端口代码的示例使用类似于我在下面显示的方法,不使用单独的线程池我提出。这就是让我觉得我错过了什么因为我数不胜数的原因!
注意:这是一个有点人为的例子,因为Windows will allow如果其中一个可运行线程进入等待状态,则会有一个额外的数据包出队;我在我的代码中使用已注释的cout
调用显示此内容:
系统还允许线程在GetQueuedCompletionStatus中等待 如果另一个正在运行的线程关联,则处理完成包 使用相同的I / O完成端口进入其他的等待状态 原因,例如SuspendThread函数。当线程在 等待状态再次开始运行,可能会有一段短暂的时间 活动线程数超过并发值。的然而, 系统通过不允许任何新活动来快速减少此数量 线程,直到活动线程数低于并发 值
但是我不会在我的线程函数中调用SuspendThread
,我不知道cout
以外的哪些函数会导致线程进入等待状态,因此我无法预测我的一个或多个线程是否会陷入困境!因此,我的线程池的想法;至少上下文切换意味着其他数据包有机会出队!
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <thread>
#include <vector>
#include <algorithm>
#include <atomic>
#include <ctime>
#include <iostream>
using namespace std;
int main()
{
HANDLE hCompletionPort1;
if ((hCompletionPort1 = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 4)) == NULL)
{
return -1;
}
vector<thread> vecAllThreads;
atomic_bool bStop(false);
// Fill our vector with 10 threads, each of which waits on our IOCP.
generate_n(back_inserter(vecAllThreads), 10, [hCompletionPort1, &bStop] {
thread t([hCompletionPort1, &bStop]()
{
// Thread body
while (true)
{
DWORD dwBytes = 0;
LPOVERLAPPED pOverlapped = 0;
ULONG_PTR uKey;
if (::GetQueuedCompletionStatus(hCompletionPort1, &dwBytes, &uKey, &pOverlapped, INFINITE) == 1)
{
if (dwBytes == 0 && uKey == 0 && pOverlapped == 0)
break; // Special completion packet; end processing.
//cout << uKey; // EVEN THIS WILL CAUSE A "wait" which causes MORE THAN 4 THREADS TO ENTER!
if (uKey >4)
cout << "Started processing packet ID > 4!" << endl;
while (!bStop)
; // INFINITE LOOP
}
}
});
return move(t);
}
);
// Queue 8 completion packets to our IOCP...only four will be processed until we set our bool
for (int i = 1; i <= 8; ++i)
{
PostQueuedCompletionStatus(hCompletionPort1, 0, i, new OVERLAPPED);
}
while (!bStop)
{
int nVal;
cout << "Enter 1 to cause current processing threads to end: ";
cin >> nVal;
bStop = (nVal == 1);
}
for (int i = 0; i < 10; ++i) // Tell all 10 threads to stop processing on the IOCP
{
PostQueuedCompletionStatus(hCompletionPort1, 0, 0, 0); // Special packet marking end of IOCP usage
}
for_each(begin(vecAllThreads), end(vecAllThreads), mem_fn(&thread::join));
return 0;
}
<小时/> 编辑#1
我的意思是&#34;单独的线程池&#34;如下所示:
class myThread {
public:
void SetTask(LPOVERLAPPED pO) { /* start processing pO*/ }
private:
thread m_thread; // Actual thread object
};
// The threads in this thread pool are not associated with the IOCP in any way whatsoever; they exist
// purely to be handed a completion packet which they then process!
class ThreadPool
{
public:
void Initialise() { /* create 100 worker threads and add them to some internal storage*/}
myThread& GetNextFreeThread() { /* return one of the 100 worker thread we created*/}
} g_threadPool;
与IOCP关联的四个线程中的每个线程随后更改为
的代码if (::GetQueuedCompletionStatus(hCompletionPort1, &dwBytes, &uKey, &pOverlapped, INFINITE) == 1)
{
if (dwBytes == 0 && uKey == 0 && pOverlapped == 0)
break; // Special completion packet; end processing.
// Pick a new thread from a pool of pre-created threads and assign it the packet to process
myThread& thr = g_threadPool.GetNextFreeThread();
thr.SetTask(pOverlapped);
// Now, this thread can immediately return to the IOCP; it doesn't matter if the
// packet we dequeued would take forever to process; that is happening in the
// separate thread thr *that will not intefere with packets being dequeued from IOCP!*
}
这样,没有办法可以在没有更多数据包出队的情况下结束!
答案 0 :(得分:0)
似乎对是否应该使用单独的线程池存在矛盾的意见。显然,正如我发布的示例代码所示,如果数据包的处理没有进入等待状态,则数据包有可能停止从IOCP中出队;给定,无限循环可能是不现实的,但它确实证明了这一点。