IO完成端口:单独的线程池来处理出队的数据包?

时间:2018-03-08 12:08:22

标签: c++11 network-programming winsock winsock2 iocp

注意:我已经为此添加了C ++标记,因为a)代码是C ++,b)使用C ++的人可能已经使用了IO完成端口。所以请不要喊。

<小时/> 我正在使用IO完成端口,最终完全理解(并经过测试,证明) - 在RbMm的帮助下 - NumberOfConcurrentThreadsCreateIoCompletionPort()参数的含义。

我有以下小程序,它创建了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!*
}

这样,没有办法可以在没有更多数据包出队的情况下结束!

1 个答案:

答案 0 :(得分:0)

似乎对是否应该使用单独的线程池存在矛盾的意见。显然,正如我发布的示例代码所示,如​​果数据包的处理没有进入等待状态,则数据包有可能停止从IOCP中出队;给定,无限循环可能是不现实的,但它确实证明了这一点。