BindIoCompletionCallback多线程下效率不如单线程高.

IOCP--Windows服务器编程                                                                              一对于IOCP,搞Windows服务器编程的都不会陌生,它所表现出来的性能是其他各种WinSock模型难望其项背的。撰写本文的目的就是为让大家能够在深入理解IOCP的基础上,再来深入的挖掘Windows系统的性能。此处假设读者对IOCP模型已 阅读详情
BindIoCompletionCallback多线程下效率不如单线程高. 2015-02-26 14:13:20

分类: C/C++

// IOCP_TCPIP_Socket_Server.cpp   
#include "stdafx.h"
#include "SafeLocker.h"  
#include <WinSock2.h>   
#include <Windows.h>
#include <Mswsock.h>
#include <vector>
#include <iostream>   
using namespace std;  
  
#pragma comment(lib, "Ws2_32.lib")      // Socket编程需用的动态链接库   
#pragma comment(lib, "Kernel32.lib")    // IOCP需要用到的动态链接库   
  
/** 
 * 结构体名称:PER_IO_DATA 
 * 结构体功能:重叠I/O需要用到的结构体,临时记录IO数据 
 **/  
 
#define BUFFSIZE (1024*10)
#define RECVSIZE (1024*10)
#define SENDSIZE (1024*5)
#define STATUS_ACCEPT 3
#define IO_TYPE_READ 5
#define IO_TYPE_WRITE 6
#define BUF_SIZE_TO_ACCEPT ((sizeof (sockaddr_in) + 16) * 2)
#define SOCKET_SIZE 20000
typedef struct{
double nDataTotal;
float fTotalLast;
int nActCount;
int nLastLen;
int TotalSize;
int ReadSize;
DWORD tBegin;
DWORD tEnd;
DWORD tDiff;
}RecvInfo;


typedef struct  
{  
    OVERLAPPED overlapped;  
    WSABUF databuff;  
    char buffer[ BUFFSIZE ];
BYTE BufferForAccept[BUF_SIZE_TO_ACCEPT];
char *pHeader;
    int TotalSize;
int ReadSize;
    int operationType;
bool bStart;
int NetSizeInfoTempLen;
BYTE NetSizeInfoTemp[NET_SIZE_INFO_LEN];


SOCKET skClient;
SOCKET skServer;
}PER_IO_OPERATEION_DATA, *LPPER_IO_OPERATION_DATA, *LPPER_IO_DATA, PER_IO_DATA;  
  
/** 
 * 结构体名称:PER_HANDLE_DATA 
 * 结构体存储:记录单个套接字的数据,包括了套接字的变量及套接字的对应的客户端的地址。 
 * 结构体作用:当服务器连接上客户端时,信息存储到该结构体中,知道客户端的地址以便于回访。 
 **/  
typedef struct  
{  
    SOCKET socket;  
    SOCKADDR_STORAGE ClientAddr;  
}PER_HANDLE_DATA, *LPPER_HANDLE_DATA;  


char *g_pRecvData;
CRITICAL_SECTION m_SlotLock;
// 定义全局变量   
const int DefaultPort = 20000;         
vector < PER_HANDLE_DATA* > clientGroup;      // 记录客户端的向量组   
  
HANDLE hMutex = CreateMutex(NULL, FALSE, NULL);  
DWORD WINAPI ServerSendThread(LPVOID IpParam);  
void  WINAPI IOWorkDummy(DWORD ExeCode, DWORD IOSize, LPOVERLAPPED pOVL);


RecvInfo g_info;
LPFN_ACCEPTEX m_lpfnAcceptEx;
// 开始主函数   
int main()  
{  
// 加载socket动态链接库   
    WORD wVersionRequested = MAKEWORD(2, 2); // 请求2.2版本的WinSock库   
    WSADATA wsaData;    // 接收Windows Socket的结构信息   
    DWORD err = WSAStartup(wVersionRequested, &wsaData);  
  
    if (0 != err){  // 检查套接字库是否申请成功   
        cerr << "Request Windows Socket Library Error!\n";  
        system("pause");  
        return -1;  
    }  
    if(LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2){// 检查是否申请了所需版本的套接字库   
        WSACleanup();  
        cerr << "Request Windows Socket Version 2.2 Error!\n";  
        system("pause");  
        return -1;  
    }  
  
// 创建IOCP的内核对象   
    /** 
     * 需要用到的函数的原型: 
     * HANDLE WINAPI CreateIoCompletionPort( 
     *    __in   HANDLE FileHandle,     // 已经打开的文件句柄或者空句柄,一般是客户端的句柄 
     *    __in   HANDLE ExistingCompletionPort, // 已经存在的IOCP句柄 
     *    __in   ULONG_PTR CompletionKey,   // 完成键,包含了指定I/O完成包的指定文件 
     *    __in   DWORD NumberOfConcurrentThreads // 真正并发同时执行最大线程数,一般推介是CPU核心数*2 
     * ); 
     **/  
    HANDLE completionPort = CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, 0, 0);  
    if (NULL == completionPort){    // 创建IO内核对象失败   
        cerr << "CreateIoCompletionPort failed. Error:" << GetLastError() << endl;  
        system("pause");  
        return -1;  
    }  
  
// 创建IOCP线程--线程里面创建线程池   
  
    // 确定处理器的核心数量   
    SYSTEM_INFO mySysInfo;  
    GetSystemInfo(&mySysInfo);  
  
    // 基于处理器的核心数量创建线程   
    
// 建立流式套接字   
    SOCKET srvSocket = socket(AF_INET, SOCK_STREAM, 0);  
  
// 绑定SOCKET到本机   
    SOCKADDR_IN srvAddr;  
    srvAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);  
    srvAddr.sin_family = AF_INET;  
    srvAddr.sin_port = htons(DefaultPort);  
    int bindResult = bind(srvSocket, (SOCKADDR*)&srvAddr, sizeof(SOCKADDR));  
    if(SOCKET_ERROR == bindResult){  
        cerr << "Bind failed. Error:" << GetLastError() << endl;  
        system("pause");  
        return -1;  
    }  
  
// 将SOCKET设置为监听模式   
    int listenResult = listen(srvSocket, 100);  
    if(SOCKET_ERROR == listenResult){  
        cerr << "Listen failed. Error: " << GetLastError() << endl;  
        system("pause");  
        return -1;  
    }  
      
// 开始处理IO数据   
    cout << "本服务器已准备就绪,正在等待客户端的接入...\n";  
  memset(&g_info, 0, sizeof(RecvInfo));


//int rcv_size = 1024*1024*10;    /* 接收缓冲区大小为8K */ 
//int optlen = sizeof(rcv_size); 
//err = setsockopt(srvSocket,SOL_SOCKET,SO_RCVBUF, (char *)&rcv_size, optlen); 
//if(err<0){ 
// printf("设置接收缓冲区大小错误\n"); 
//}else{
// printf("设置接收缓冲区大小OK\n"); 
//}


g_pRecvData = new char[1024*1024*500];
if (g_pRecvData==NULL){
printf("new g_pRecvData false\n");
}
    // 创建用于发送数据的线程   
    //HANDLE sendThread = CreateThread(NULL, 0, ServerSendThread, 0, 0, NULL);  
if (0 == BindIoCompletionCallback((HANDLE)srvSocket, IOWorkDummy, 0))
{
printf("BindIoCompletionCallback is failed. Error code = %d", GetLastError());
}


GUID GuidAcceptEx = WSAID_ACCEPTEX;
DWORD dwBytes = 0;
if (SOCKET_ERROR == WSAIoctl(srvSocket, SIO_GET_EXTENSION_FUNCTION_POINTER,
&GuidAcceptEx, sizeof (GuidAcceptEx),
&m_lpfnAcceptEx, sizeof (m_lpfnAcceptEx), &dwBytes, 0, 0))
{
printf("WSAIoctl is failed. Error code = %d", WSAGetLastError());
}


InitializeCriticalSection(&m_SlotLock);
int nPackIndex = 0;
    for(int i=0; i<SOCKET_SIZE; i++)
{  
        PER_HANDLE_DATA * PerHandleData = NULL;  
        SOCKADDR_IN saRemote;  
        int RemoteLen;  
        SOCKET acceptSocket;  
  
        // 接收连接,并分配完成端,这儿可以用AcceptEx()   
        RemoteLen = sizeof(saRemote);  
        //acceptSocket = accept(srvSocket, (SOCKADDR*)&saRemote, &RemoteLen);  
acceptSocket = ::WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP, NULL, 0,WSA_FLAG_OVERLAPPED);


        if(SOCKET_ERROR == acceptSocket){   // 接收客户端失败   
            cerr << "Accept Socket Error: " << GetLastError() << endl;  
            system("pause");  
            return -1;  
        }  
          
        // 创建用来和套接字关联的单句柄数据信息结构   
        PerHandleData = (LPPER_HANDLE_DATA)GlobalAlloc(GPTR, sizeof(PER_HANDLE_DATA));  // 在堆中为这个PerHandleData申请指定大小的内存   
        PerHandleData -> socket = acceptSocket;  
        memcpy (&PerHandleData -> ClientAddr, &saRemote, RemoteLen);  
        clientGroup.push_back(PerHandleData);       // 将单个客户端数据指针放到客户端组中   
  
          
        // 开始在接受套接字上处理I/O使用重叠I/O机制   
        // 在新建的套接字上投递一个或多个异步   
        // WSARecv或WSASend请求,这些I/O请求完成后,工作者线程会为I/O请求提供服务       
        // 单I/O操作数据(I/O重叠)   
        LPPER_IO_OPERATION_DATA PerIoData = NULL;  
        PerIoData = (LPPER_IO_OPERATION_DATA)GlobalAlloc(GPTR, sizeof(PER_IO_OPERATEION_DATA));  
        ZeroMemory(&(PerIoData->overlapped), sizeof(OVERLAPPED));  
        PerIoData->pHeader = g_pRecvData + nPackIndex*RECVSIZE;
PerIoData->databuff.len = RECVSIZE;  
PerIoData->databuff.buf = PerIoData->pHeader;
        PerIoData->operationType = STATUS_ACCEPT;    // read  
PerIoData->skServer = srvSocket;
PerIoData->skClient = acceptSocket;
PerIoData->bStart = true;
PerIoData->TotalSize = 0;
PerIoData->ReadSize = 0;
nPackIndex++;
  
        DWORD RecvBytes;  
        DWORD Flags = 0;  
// 将接受套接字和完成端口关联   
m_lpfnAcceptEx(srvSocket, PerHandleData->socket, reinterpret_cast<void*>(const_cast<BYTE*>(PerIoData->BufferForAccept)), 
0,//将接收缓冲置为0,令AcceptEx直接返回,防止拒绝服务攻击
sizeof(sockaddr_in) + 16, sizeof(sockaddr_in) + 16, NULL,
(LPOVERLAPPED)PerIoData);


if (0 == BindIoCompletionCallback((HANDLE)PerHandleData->socket, IOWorkDummy, 0))
{
printf("BindIoCompletionCallback is failed. Error code = %d", GetLastError());
}
    }  
g_info.tBegin = GetTickCount();
    system("pause"); 
// free resource


DeleteCriticalSection(&m_SlotLock);
for(int i=0; i<SOCKET_SIZE; i++)
{ 
//GlobalFree
}
free(g_pRecvData);
    return 0;  
}


int Put(LPPER_IO_DATA PerIoData, BYTE *pDataPartial, DWORD IOSize)
{
BOOL bError = 0;
BYTE *pDataStart;
DWORD IOSizeRevised; //不带数据长度的数据大小(IOSize-4)
DWORD SizeToRead; //收到实际的数据大小
DWORD SizeRemain; //若包完整后还有数据的长度
BYTE *pDataIndex;
int NetSizeRevised; //数据长度位数
BOOL bDataChain; //数据是否

pDataIndex = pDataPartial;
do
{
// 1. 首次来包
if (PerIoData->bStart) // Don't forget '&'
{
PerIoData->bStart = false;
if (0 < PerIoData->NetSizeInfoTempLen)
{
NetSizeRevised = NET_SIZE_INFO_LEN - PerIoData->NetSizeInfoTempLen;
memcpy(&PerIoData->TotalSize, PerIoData->NetSizeInfoTemp, PerIoData->NetSizeInfoTempLen);
if (PerIoData->TotalSize>1024*512){// Error
printf("sockId1=0x%x, len=%d\n", PerIoData, PerIoData->TotalSize);
bError = 1;
goto ErrHand;
}
memcpy(&PerIoData->TotalSize + PerIoData->NetSizeInfoTempLen, pDataIndex, NetSizeRevised);
PerIoData->NetSizeInfoTempLen = 0; // initialize it.
}
else
{
// 2. 包头长度
NetSizeRevised = NET_SIZE_INFO_LEN;
memcpy(&PerIoData->TotalSize, pDataIndex, NetSizeRevised);
if (PerIoData->TotalSize>1024*512){// Error
printf("sockId2=0x%x, len=%d\n", PerIoData, PerIoData->TotalSize);
bError = 1;
goto ErrHand;
}
}

PerIoData->ReadSize = 0;
// 5. 数据位置(无数据长度)、数据大小
pDataStart = pDataIndex + NetSizeRevised; //
IOSizeRevised = IOSize - NetSizeRevised;
bDataChain = 0;
}
else
{
pDataStart = pDataIndex;
IOSizeRevised = IOSize;
bDataChain = 1;
}


if ((PerIoData->TotalSize - PerIoData->ReadSize) >= IOSizeRevised)
{
SizeToRead = IOSizeRevised;
SizeRemain = 0;
}
else
{
SizeToRead = PerIoData->TotalSize - PerIoData->ReadSize;
SizeRemain = IOSizeRevised - SizeToRead;
}
if (PerIoData->TotalSize>1024*512){ // Error trace
printf("sockId3=0x%x, len=%d\n", PerIoData, PerIoData->TotalSize);
bError = 1;
goto ErrHand;
}


//if (pBuffSock->BufferReadIORealDataHeader == pBuffSock->pFillingSlot->pData){ //first
// memcpy(pBuffSock->pFillingSlot->pData, pDataStart, SizeToRead);
// if (pBuffSock->pFillingSlot->TotalSize > SizeToRead){ //只有接收数据大于BUFFER_UNIT_SIZE
// pBuffSock->pFillingSlot->pData += SizeToRead;
// pBuffSock->BufferReadIO.buf = (char *)pBuffSock->pFillingSlot->pData;
// pBuffSock->BufferReadIO.len = pBuffSock->pFillingSlot->TotalSize - SizeToRead;
// }
//}else{
// if (pBuffSock->BufferReadIO.len == SizeToRead){ //restore
// pBuffSock->pFillingSlot->pData = pBuffSock->BufferReadIORealDataHeader;
// pBuffSock->BufferReadIO.buf = (char *)pBuffSock->BufferReadIOHeader;
// pBuffSock->BufferReadIO.len = sizeof (pBuffSock->BufferReadIORealData);
// }
//}
PerIoData->ReadSize += SizeToRead;


if (PerIoData->TotalSize == PerIoData->ReadSize && PerIoData->ReadSize>100)
{
//printf("xxxxxxxxx=0x%x, clientSock=%d, TotalSize=%d, ReadSize=%d\n", PerIoData, PerIoData->skClient, PerIoData->TotalSize, PerIoData->ReadSize);
g_info.TotalSize=PerIoData->TotalSize;
g_info.ReadSize=PerIoData->ReadSize;
PerIoData->TotalSize = PerIoData->ReadSize = 0;
PerIoData->bStart = true;
}


if (0 == SizeRemain)
{
break;
}
else if (NET_SIZE_INFO_LEN < SizeRemain)
{
IOSize = SizeRemain;
if (0 == bDataChain)
{
pDataIndex += (NET_SIZE_INFO_LEN + SizeToRead);
}
else
{
pDataIndex += SizeToRead;
}
}
else if (0 < SizeRemain && NET_SIZE_INFO_LEN >= SizeRemain)
{
memcpy(PerIoData->NetSizeInfoTemp, pDataStart + SizeToRead, SizeRemain);
PerIoData->NetSizeInfoTempLen = SizeRemain;
break;
}
else
{
printf("Odd case.");
break;
}
} while (0 < SizeRemain);


ErrHand:
return (1 != bError);
} // Put()


// 开始服务工作线程函数   
void WINAPI IOWorkDummy(DWORD ExeCode, DWORD BytesTransferred, LPOVERLAPPED pOVL)
{
LPOVERLAPPED IpOverlapped;  
LPPER_HANDLE_DATA PerHandleData = NULL;  
LPPER_IO_DATA PerIoData = NULL;  
DWORD RecvBytes;  
DWORD Flags = 0;  
BOOL bRet = false;  


PerIoData = (LPPER_IO_DATA)CONTAINING_RECORD(pOVL, PER_IO_OPERATEION_DATA, overlapped);
// 检查在套接字上是否有错误发生   
/*static int oldPId = 0;
if (oldPId != GetCurrentThreadId()) {
printf("pid = %d\n", GetCurrentThreadId());
oldPId = GetCurrentThreadId();
}*/
if (PerIoData->operationType == STATUS_ACCEPT){
int nRet = ::setsockopt(
PerIoData->skClient,SOL_SOCKET,SO_UPDATE_ACCEPT_CONTEXT,
(char *)&PerIoData->skServer,sizeof(SOCKET));
if (nRet != 0){
printf("setsockopt error=%d\n", GetLastError());
}else{
PerIoData->operationType = IO_TYPE_READ;
}
}else if (PerIoData->operationType == IO_TYPE_READ){
if(0 == BytesTransferred){  
//closesocket(PerHandleData->socket);  
//GlobalFree(PerHandleData);  
//GlobalFree(PerIoData);  
//continue;  
// 为下一个重叠调用建立单I/O操作数据   
ZeroMemory(&(PerIoData->overlapped), sizeof(OVERLAPPED)); // 清空内存   
PerIoData->operationType = IO_TYPE_READ;


} else{
// 开始数据处理,接收来自客户端的数据   
//cout << "A Client says: " << PerIoData->databuff.len << endl;  
//OSafeLocker SLock(&m_SlotLock);
//SLock.Lock();
//if (PerIoData->bStart){
// // header1
// PerIoData->bStart = false;
// PerIoData->TotalSize = *((int *)PerIoData->databuff.buf);
// printf("PerIoData=0x%x, clientSock=%d, TotalSize=%d\n", PerIoData, PerIoData->skClient, PerIoData->TotalSize);
// if (PerIoData->TotalSize>1024*2000 || PerIoData->TotalSize<0){
// //fwrite( g_pBuf, 1, g_lastLen, g_hFile );
// //sprintf(g_pBuf, "Error TotalSize2 = %d\n", PerIoData->TotalSize);
// //fwrite( g_pBuf, 1, strlen(g_pBuf), g_hFile );
// //fflush( g_hFile );
// printf("Error TotalSize2 = %d\n", PerIoData->TotalSize);
// //break;
// }


// PerIoData->ReadSize = BytesTransferred - 4;
//}else{
// PerIoData->ReadSize += BytesTransferred;
//}
//g_info.nDataTotal += BytesTransferred;


////PerIoData->buff[len] = '\0';
//if (PerIoData->TotalSize <= PerIoData->ReadSize && PerIoData->TotalSize>100){
// printf("xxxxxxxxx=0x%x, clientSock=%d, TotalSize=%d, ReadSize=%d\n", PerIoData, PerIoData->skClient, PerIoData->TotalSize, PerIoData->ReadSize);
// g_info.TotalSize=PerIoData->TotalSize;
// g_info.ReadSize=PerIoData->ReadSize;
// PerIoData->TotalSize = PerIoData->ReadSize = 0;
// PerIoData->bStart = true;
// //printf("full .... ");
//}
//SLock.Unlock();
//


g_info.nDataTotal += BytesTransferred;
Put(PerIoData, (BYTE *)PerIoData->databuff.buf, BytesTransferred);
if (g_info.nActCount++>45000){
g_info.nActCount = 0;
g_info.tEnd = GetTickCount();
g_info.tDiff = g_info.tEnd - g_info.tBegin;


float fTotal = (float)g_info.nDataTotal/(1024*1024);
float nDiffTime = (float)g_info.tDiff/1000;
g_info.tBegin = g_info.tEnd;
printf("tm=%5.3f, v=%5.3f, Total=%5.2f, UT=%d, UR=%d \n", 
nDiffTime, (fTotal-g_info.fTotalLast)/nDiffTime, fTotal, g_info.TotalSize, g_info.ReadSize); 
g_info.fTotalLast = fTotal;
}


// 为下一个重叠调用建立单I/O操作数据   
ZeroMemory(&(PerIoData->overlapped), sizeof(OVERLAPPED)); // 清空内存   
PerIoData->operationType = IO_TYPE_READ;
}
}
Flags = MSG_PARTIAL;
WSARecv(PerIoData->skClient, &(PerIoData->databuff), 1, &RecvBytes, &Flags, &(PerIoData->overlapped), NULL);  
} // IOWorkDummy()
  
// 发送信息的线程执行函数   
DWORD WINAPI ServerSendThread(LPVOID IpParam)  
{  
    while(1){  
        char talk[200];  
        gets(talk);  
        int len;  
        for (len = 0; talk[len] != '\0'; ++len){  
            // 找出这个字符组的长度   
        }  
        talk[len] = '\n';  
        talk[++len] = '\0';  
        printf("I Say:");  
        cout << talk;  
        WaitForSingleObject(hMutex,INFINITE);  
        for(int i = 0; i < clientGroup.size(); ++i){  
            send(clientGroup[i]->socket, talk, 200, 0);  // 发送信息   
        }  
        ReleaseMutex(hMutex);   
    }  
    return 0;  
}  

以上是Server端测试程序,不带锁的,在xp下BindIoCompletionCallback为单线程,在发送每包大小25KB下,接收速度为330MB/s, 但在win8下为多线程(4线程),最高也只有200MB/s
线程切换能相差这么大? Client端程序不变的情况下测试的结果.

函数: int Put(LPPER_IO_DATA PerIoData, BYTE *pDataPartial, DWORD IOSize)处理粘包时不严谨
接收速度 330MB/s计算有问题,有空验证之后纠正
普冉PY32F003定时器实战:从HSE时钟配置到500ms精准闪烁LED(附完整代码) 本文详细解析了普冉PY32F003单片机定时器的实战应用,从HSE时钟配置到实现500ms精准LED闪烁。通过完整的代码示例和深入的技术分析,帮助开发者掌握定时器参数计算、中断优先级管理及调试技巧,适用于各类嵌入式开发项目。 阅读详情

相关推荐

DolphinScheduler租户配置踩坑实录:手把手教你修复‘tenant not exists‘报错

本文深度解析DolphinScheduler的租户模型,并提供一套从界面到数据库的系统性排查流程,手把手教你修复常见的'tenant not exists'报错。文章揭示了该错误的本质是用户与租户数据关联不一致,并给出了通过UI或SQL更新的具体修复方案,同时分享了根因分析与预防措施,帮助用户从根本上理解和避免此类问题。

2w3e4r5t6y的博客 541

BindIoCompletionCallback

四、编写线程池回调函数:   在讨论扩展定义OVERLAPPED结构体时,给出了非线程池版的线程函数的大概框架,也就是传统IOCP使用的自建线程使用方式,这种方式要自己创建完成端口句柄,自己将SOCKET句柄绑定到完成端口,这里就不在赘述,主要介绍下调用BindIoCompletionCallback函数时,应如何编写这个线程池的回调函数,其实它与前面那个线程函数是很类似的。先来看看回调函数长

linuxheik的专栏 1802

从原理到实践:详解RGBD相机深度与彩色图像对齐(D2C)算法

本文深入解析了RGBD相机深度与彩色图像对齐(D2C)算法的原理与实现。针对自组装相机中深度图与彩色图不匹配的核心痛点,文章从相机模型、坐标系变换等数学基础讲起,剖析了传统点映射算法的缺陷,并重点介绍了高效的矩形区域映射算法。该算法通过将深度像素视为矩形区域进行映射,有效解决了空洞和遮挡问题,并附有可直接复用的C++代码与优化技巧,为三维重建、机器人视觉等应用奠定坚实基础。

weixin_28363123的博客 241

IOCP编程之“双节棍”

在我博客之前的一些文章中,我讨论了关于使用BindIoCompletionCallback函数编写IOCP服务器的话题,在之后的一段时间中我也用此函数展开了伟大的服务器编程实践活动,在实际的应用中,我发现这个函数的很多怪脾气,今天我觉得有必要为大家澄清一下关于此函数的种种诽谤和传闻。其实这是一个非常非常好用的函数,直接利用了Windows系统所有的优秀特性于一身——多线程、线程池、IOCP

blackbean的专栏 1170

ais信号接收设备_国内首款基于软件无线电技术的AIS基站投入测试使用

星网宇达子公司北京凯盾环宇科技有限公司经过多年的潜心研发,成功研制出国内首台基于软件无线电(SDR)的CATON 9011 AIS基站。该基站采用软件无线电技术,利用软件数字技术代替传统的模拟电路,大大的降低了模拟器件的使用率,提高了基站的可靠性和使用年限。CATON 9011 AIS基站研制依据ITU-R M.1371-5、IEC 62320-1、IEC 61162-1等国际标准,兼容...

weixin_29057163的博客 2334

KV Cache 详解:新手也能理解的 LLM 推理加速技巧

(Autoregressive)生成的,即每次生成一个 token,然后将其作为输入继续生成下一个 token。:如果序列太长,超过了 KV Cache 的存储限制,就需要丢弃最早的 tokens(类似滑动窗口)。时,每次只会生成一个新 token,然后再把这个 token 作为输入继续生成下一个 token。,只缓存计算过的 K,VK, V,每次只计算新 token 的 K,VK, V,从而提升推理效率。:如果输入的 prompt 发生变化,之前的 KV Cache 不能复用,必须重新计算。

Harry的博客 7356

异步机制 - BindIoCompletionCallback

直接上代码 VOID CALLBACK test_io_completion_routine( DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped ) { printf("dwErrorCode %d, dwNumberOfBytesTransfere...

weixin_30293079的博客 231

新政下的绿电直连项目经济性分析:模式创新与价值重构

随着全球气候治理与低碳转型进程不断加速,绿电直连作为新能源就近消纳的创新模式,正成为破解新能源消纳瓶颈、规避碳关税壁垒的核心路径。2025年9月,国家发展改革委、国家能源局发布《关于完善价格机制促进新能源发电就近消纳的通知》(1192号文),为绿电直连项目的经济性分析与效益测算提供了明确的政策依据。本文将基于新政框架,深入解析绿电直连项目的经济性影响因素、盈利模式与优化路径,并结合案例与量化模型,为项目投资决策提供系统参考。

getusushu的博客 138

IOCP+WinSock2新函数打造高性能SOCKET池

在前一篇文章《WinSock2编程之打造完整的SOCKET池 》中,介绍了WinSock2的一些新函数,并重点详细介绍了什么是SOCKET池,有了这个概念,现在就接着展开更深入的讨论。首先这里要重点重申一下就是,SOCKET池主要指的是使用面向连接的协议的情况下,最常用的就是需要管理大量的TCP连接的时候。常见的就是Web服务器、FTP服务器等。下面就分步骤的详细介绍如何最终实现SOC

blackbean的专栏 638

HarmonyOS 鸿蒙开发DevEco Studio OpenHarmony:安全测试

通过对HarmonyOS应用生命周期建模和应用攻击面建模,采用静态数据流分析技术,提高漏洞发现的准确率,同时覆盖20余种攻击面,65+漏洞测试项,帮助开发者提前发现和识别漏洞隐患。在检测报告中,会针对每一项漏洞风险项给出明确的修复建议,可以帮助开发者快速修复漏洞。

斌擎科技 1181

雨雾天YOLOv8检测拉胯?暗通道去雾+上下文注意力双优化,mAP从58%飙到79%,暴雨也能看清车牌

室外交通监控:高速、省道的雨雾天检测,重点解决小车、货车漏检;车载ADAS系统:雨天行车时的前方车辆、行人识别,需实时性(<40ms);隧道出入口:明暗交界+雾天的复杂场景,暗通道能平衡亮度。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 1380

IOCP编程之基本原理

在我的博客之前写了很多关于IOCP的“行云流水”似的看了让人发狂的文章,尤其是几篇关于IOCP加线程池文章,更是让一些功力不够深厚的初学IOCP者,有种吐血的感觉。为了让大家能够立刻提升内力修为,并且迅速的掌握IOCP这个Windows平台上的乾坤大挪移心法,这次我决定给大家好好补补这个基础。 要想彻底征服IOCP,并应用好IOCP这个模型,首先就让我们穿越到遥远的计算机青铜器时代(以出现PC为

zgx123666的专栏 3876

Flutter:如何在Android中实现串口通信调试

本文介绍如何通过flutter_libserialport插件在Flutter中实现串口通信调试。

sg_knight的专栏 6098

IOCP编程之“双节棍” (转)

在我博客之前的一些文章中,我讨论了关于使用BindIoCompletionCallback函数编写IOCP服务器的话题,在之后的一段时间中我也用此函数展开了伟大的服务器编程实践活动,在实际的应用中,我发现这个函数的很多怪脾气,今天我觉得有必要为大家澄清一下关于此函数的种种诽谤和传闻。 其实这是一个非常非常好用的函数,直接利用了Windows系统所有的优秀特性于一身——多线程、线程池、IOCP等等...

weixin_34122810的博客 102

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

IOCP进阶文4篇

注:以下IOCP系列博文版权归 网易博客 GameBaby 所有,转载请按如下方式显示标明原创作者及原文出处,以示尊重!!原创作者:GameBaby原文出处:http://gamebabyrocksun.blog.163.com/IOCP加Windows线程池打造高伸缩性高性能的服务器应用       对于IOCP,搞Windows服务器编程的都不会陌生,它所表现出来的性能是其他各种WinSock模型难望其项背的。撰写本文的目的就是为让大家能够在深入理解IOCP的基础上,再来深入的挖掘Windows系统的性

优秀技术文章收录 9229

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

IOCP加Windows线程池打造高伸缩性高性能的服务器应用

  对于IOCP,搞Windows服务器编程的都不会陌生,它所表现出来的性能是其他各种WinSock模型难望其项背的。撰写本文的目的就是为让大家能够在深入理解IOCP的基础上,再来深入的挖掘Windows系统的性能。此处假设读者对IOCP模型已经深刻理解,并对Windows线程、线程池有一定的了解。如果对此还不熟悉,限于篇幅的原因,请您先学习理解这些内容后再来阅读本文。       在IOCP模型

wzwind的专栏 1323

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

IOCP加Windows线程池打造高伸缩性高性能的服务

对于IOCP,搞Windows服务器编程的都不会陌生,它所表现出来的性能是其他各种WinSock模型难望其项背的。撰写本文的目的就是为让大家能够在深入理解IOCP的基础上,再来深入的挖掘Windows系统的性能。此处假设读者对IOCP模型已经深刻理解,并对Windows线程、线程池有一定的了解。如果对此还不熟悉,限于篇幅的原因,请您先学习理解这些内容后再来阅读本文。        在IOCP模型

多媒体编程、网络编程、系统编程、网络安全编程、驱动编程 856

Windows核心编程 第十一章 线程池的使用

Windows核心编程 第十一章 线程池的使用

天使也掉毛 676

老版VC++线程池

在一般的设计中,当需要一个线程时,就创建一个,但是当线程过多时可能会影响系统的整体效率,这个性能的下降主要体现在:当线程过多时在线程间来回切换需要花费时间,而频繁的创建和销毁线程也需要花费额外的机器指令,同时在某些时候极少数线程可能就可以处理大量,比如http服务器可能只需要几个线程就可以处理用户发出的http请求,毕竟相对于用户需要长时间来阅读网页来说,CPU只是找到对应位置的页面返回即可。在这

Masimaro的专栏 596

socket连接池

socket连接池  2011-06-16 20:40:54|  分类: 网络服务编程技术 |  标签: |字号大中小 订阅 socket连接池 SOCKET连接池原来注意过,但时间长了,对这个的了解有些乱,今天总结一下,趁着天气比较凉快,心情也比较舒畅。 SOCKET连接池产生,目的是为了减少内核在创建和销毁SOCKET时所产生的开销,一个两个的SOCKET的

nokianasty的专栏 1万+

java 线程池 完成端口_I/O完成端口、异步I/O、APC和线程池(四)——线程池

下面摘抄于MSDN《ThreadPooling》。有许多应用程序创建的线程花费了大量时间在睡眠状态来等待事件的发生。还有一些线程进入睡眠状态后定期被唤醒以轮询工作方式来改变或者更新状态信息。线程池可以让你更有效地使用线程,它为你的应用程序提供一个由系统管理的工作者线程池。至少会有一个线程来监听放到线程池的所有等待操作,当等待操作完成后,线程池中将会有一个工作者线程来执行相应的回调函数。你也可以把...

weixin_39846089的博客 174

windows2000提供了如下几种线程池函数用于线程管理

windows2000提供了如下几种线程池函数用于线程管理:一、异步调用函数:BOOL QueueUserWorkItem(PTHREAD_START_ROUTINE pfnCallback,PVOID pvContext,ULONG dwFlags);该函数将“工作项目”放入线程池并且立即返回。工作项目是指一个用pfnCallback参数标识的函数。它被调用并且传递单个参数pvContext

zkprh418的专栏 859

(转载)异步IO、APC、IO完成端口、线程池与高性能服务器 (四)

转载: http://www.vchelp.net/原作者姓名 Fang(fangguicheng@21cn.com) 正文异步IO、APC、IO完成端口、线程池与高性能服务器之四 线程池线程池下面摘抄于MSDN《Thread Pooling》。有许多应用程序创建的线程花费了大量时间在睡眠状态来等待事件的发生。还有一些线程进入睡眠状态后定期被唤醒以轮询工作方式来改变或者更新状态信息。线程池可以让你

Kendiv's Box 2485
上一篇: IOCP机制与网络代理服务器实现方法
下一篇: SetFileCompletionNotificationModes FILE_SKIP_COMPLETION_PORT_ON_SUCCESS
linuxheik
博客等级 码龄15年 865粉丝 135原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值