Windows下高精准延时任务的最佳实践案例(可以精确到1ms)

xingyun86 2021-4-24 1544

Windows下高精准延时任务的最佳实践案例(可以精确到1ms)

最优代码:

#define  _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <iostream>
#include <winsock2.h>
#pragma comment(lib,"ws2_32")
#include <mmsystem.h>
#pragma comment(lib,"winmm")

static uint64_t num = 0;
static SOCKET m_udp_socket_fd = -1;
static struct sockaddr_in m_dest_addr = { 0 };
#include <thread>
#include <map>
std::map<uint64_t, uint8_t> uMap = {};
static uint64_t uCurrTime = 0, uLastTime = 0;

void InitUdpSock()
{
	m_udp_socket_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (m_udp_socket_fd == -1)
	{
		perror("socket failed!\n");
		return;
	}
	//设置目的IP地址
	uint16_t port = 12345;
	const std::string& host = "2.168.1.50";
	m_dest_addr.sin_family = AF_INET;//使用IPv4协议
	m_dest_addr.sin_port = htons(port);//设置接收方端口号
	m_dest_addr.sin_addr.s_addr = inet_addr(host.c_str()); //设置接收方IP
}
void ExitUdpSock()
{
	closesocket(m_udp_socket_fd);
}
std::time_t GetTimestamp()
{
	return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now()).time_since_epoch()).count();
}
void StartSendCpp11()
{
	uCurrTime = GetTimestamp();
	if (uLastTime != uCurrTime)
	{
		uLastTime = uCurrTime;
		char cData[128] = { 0 };
		snprintf(cData, 127, "==========================%lld", num++);
		sendto(m_udp_socket_fd, cData, strlen(cData), 0, (struct sockaddr*)&m_dest_addr, sizeof(m_dest_addr));
	}
}
int cpp11_testmain()
{
	auto t = std::thread([]() {
		while (true)
		{
			StartSendCpp11();
			std::this_thread::sleep_for(std::chrono::nanoseconds(100)); //当前线程挂起一毫秒
		}
		});
	while (true)
	{
		printf("go...\n");
		std::this_thread::sleep_for(std::chrono::nanoseconds(100)); //当前线程挂起一毫秒
	}
	t.join();
	return 0;
}

int main(int argc, char** argv)
{
	WSADATA wsadata = { 0 };
	(void)WSAStartup(MAKEWORD(2, 2), &wsadata);
	InitUdpSock();
	{
		//cpp11 thread sleep方法(精确:1ms)
		cpp11_testmain();
	
	ExitUdpSock();
	WSACleanup();
	return 0;
}

优化代码:

#define  _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <iostream>

#include <winsock2.h>
#pragma comment(lib,"ws2_32")
#include <mmsystem.h>
#pragma comment(lib,"winmm")

//定义时钟分辨率,以ms为单位
#define TIMER_ACCURACY        1

MMRESULT        g_mmTimerId = 0;
UINT            g_wAccuracy = 0;

static uint64_t num = 0;
static SOCKET m_udp_socket_fd = -1;
static struct sockaddr_in m_dest_addr = { 0 };

void InitUdpSock()
{
    m_udp_socket_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (m_udp_socket_fd == -1)
    {
        perror("socket failed!\n");
        return;
    }
    //设置目的IP地址
    uint16_t port = 12345;
    const std::string& host = "2.168.1.50";
    m_dest_addr.sin_family = AF_INET;//使用IPv4协议
    m_dest_addr.sin_port = htons(port);//设置接收方端口号
    m_dest_addr.sin_addr.s_addr = inet_addr(host.c_str()); //设置接收方IP
}

void ExitUdpSock()
{
    closesocket(m_udp_socket_fd);
}

void StartSend()
{
    char cData[128] = { 0 };
    snprintf(cData, 127, "%lld", num++);
    sendto(m_udp_socket_fd, cData, strlen(cData), 0, (struct sockaddr*)&m_dest_addr, sizeof(m_dest_addr));
}

void CALLBACK TimerProc(UINT nID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD d2)
{
    if (nID == g_mmTimerId)
    {
        StartSend();
    }
}

// 释放定时器
void FreeHighTimer()
{
    if (g_mmTimerId == 0)
    {
        return;
    }
    timeKillEvent(g_mmTimerId);
    timeEndPeriod(g_wAccuracy);
}

// 初始化高精度定时器
BOOL InitHighTimer()
{
    TIMECAPS tc = { 0 };
    //利用函数timeGetDeVCaps取出系统分辨率的取值范围,如果无错则继续; 
    if (timeGetDevCaps(&tc, sizeof(TIMECAPS)) == TIMERR_NOERROR)
    {
        //分辨率的值不能超出系统的取值范围
        g_wAccuracy = min(max(tc.wPeriodMin, TIMER_ACCURACY), tc.wPeriodMax);

        //调用timeBeginPeriod函数设置定时器的分辨率 
        timeBeginPeriod(g_wAccuracy);

        // 设定1毫秒定时器
        g_mmTimerId = timeSetEvent(1, 0, TimerProc, NULL, TIME_PERIODIC);
        if (g_mmTimerId == 0)
        {
            std::cout << "timeSetEvent failed: %d" << GetLastError() << std::endl;
            return FALSE;
        }

        return TRUE;
    }

    return FALSE;
}

int main(int argc, char** argv)
{
    WSADATA wsadata = { 0 };
    (void)WSAStartup(MAKEWORD(2, 2), &wsadata);
    InitUdpSock();
    InitHighTimer();
    (void)getchar();
    FreeHighTimer();
    ExitUdpSock();
    WSACleanup();
    return 0;
}

带有高精度延迟计时函数的代码:

#define  _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <iostream>

#include <winsock2.h>
#pragma comment(lib,"ws2_32")
#include <mmsystem.h>
#pragma comment(lib,"winmm")

// 休眠指定毫秒数
void MSleep(long lTime)
{
    LARGE_INTEGER litmp;
    LONGLONG QPart1, QPart2;
    double dfMinus, dfFreq, dfTim, dfSpec;
    QueryPerformanceFrequency(&litmp);
    dfFreq = (double)litmp.QuadPart;
    QueryPerformanceCounter(&litmp);
    QPart1 = litmp.QuadPart;
    dfSpec = 0.000001 * lTime;

    do
    {
        QueryPerformanceCounter(&litmp);
        QPart2 = litmp.QuadPart;
        dfMinus = (double)(QPart2 - QPart1);
        dfTim = dfMinus / dfFreq;
    } while (dfTim < dfSpec);
}

//定义1ms和2s时钟间隔,以ms为单位
#define ONE_MILLI_SECOND    1

//定义时钟分辨率,以ms为单位
#define TIMER_ACCURACY        1

volatile DWORD    g_nCounter = 0;
volatile DWORD    g_nCnt = 0;
UINT64            g_nTicket = 0;
LARGE_INTEGER    g_xliPerfFreq = { 0 };
LARGE_INTEGER    g_xliPerfStart = { 0 };
LARGE_INTEGER    g_xliPerfNow = { 0 };
int                g_nSecond = 0;

MMRESULT        g_mmTimerId = 0;
UINT            g_wAccuracy = 0;

static uint64_t num = 0;
static SOCKET m_udp_socket_fd = -1;
static struct sockaddr_in m_dest_addr = { 0 };
void InitUdpSock()
{
    m_udp_socket_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (m_udp_socket_fd == -1)
    {
        perror("socket failed!\n");
        return;
    }
    //设置目的IP地址
    uint16_t port = 12345;
    const std::string& host = "2.168.1.50";
    m_dest_addr.sin_family = AF_INET;//使用IPv4协议
    m_dest_addr.sin_port = htons(port);//设置接收方端口号
    m_dest_addr.sin_addr.s_addr = inet_addr(host.c_str()); //设置接收方IP
}
void ExitUdpSock()
{
    closesocket(m_udp_socket_fd);
}
// 自己去实现一个PING函数, 网上大把就不发了
int StartSend()
{
    char cData[128] = { 0 };
    snprintf(cData, 127, "%lld", num++);
    sendto(m_udp_socket_fd, cData, strlen(cData), 0, (struct sockaddr*)&m_dest_addr, sizeof(m_dest_addr));
    return 1;
}

void CALLBACK TimerProc(UINT nID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD d2)
{
    //double        dtime = 0.0f;
    //char        szBuffer[MAX_PATH] = { 0x00 };
    //UINT64        nTemp = 0;

    if (nID == g_mmTimerId)
    {
        //g_nCounter++;
        g_nSecond += StartSend();
        /*if (g_nCounter == 24)
        {
            QueryPerformanceCounter(&g_xliPerfNow);
            dtime = ((double)(g_xliPerfNow.QuadPart - g_xliPerfStart.QuadPart) * 1000000.0f) / (double)g_xliPerfFreq.QuadPart;

            if (dtime < 1000000.0f)
            {
                MSleep((1000000.0f - dtime));
            }

            QueryPerformanceCounter(&g_xliPerfNow);
            dtime = ((double)(g_xliPerfNow.QuadPart - g_xliPerfStart.QuadPart) * 1000000.0f) / (double)g_xliPerfFreq.QuadPart;

            nTemp = GetTickCount64() - g_nTicket;
            snprintf(szBuffer, sizeof(szBuffer)/sizeof(*szBuffer) - 1, " [%04d] 执行时间 %lld 毫秒,  \t%.9f 微秒,  \t延时: %d ms", g_nCnt, nTemp, dtime, g_nSecond);
            std::cout << szBuffer << std::endl;

            g_nTicket = GetTickCount64();
            memset(&g_xliPerfNow, 0x00, sizeof(LARGE_INTEGER));
            memset(&g_xliPerfStart, 0x00, sizeof(LARGE_INTEGER));
            memset(&g_xliPerfNow, 0x00, sizeof(LARGE_INTEGER));

            QueryPerformanceFrequency(&g_xliPerfFreq);
            QueryPerformanceCounter(&g_xliPerfStart);

            g_nCnt++;
            g_nCounter = 0;
            g_nSecond = 0;
        }*/
    }
}

// 释放定时器
void FreeHighTimer()
{
    if (g_mmTimerId == 0)
        return;

    timeKillEvent(g_mmTimerId);
    timeEndPeriod(g_wAccuracy);
}

// 初始化高精度定时器
BOOL InitHighTimer()
{
    TIMECAPS    tc;

    QueryPerformanceFrequency(&g_xliPerfFreq);
    QueryPerformanceCounter(&g_xliPerfStart);
    g_nTicket = GetTickCount64();

    //利用函数timeGetDeVCaps取出系统分辨率的取值范围,如果无错则继续; 
    if (timeGetDevCaps(&tc, sizeof(TIMECAPS)) == TIMERR_NOERROR)
    {
        //分辨率的值不能超出系统的取值范围
        g_wAccuracy = min(max(tc.wPeriodMin, TIMER_ACCURACY), tc.wPeriodMax);

        //调用timeBeginPeriod函数设置定时器的分辨率 
        timeBeginPeriod(g_wAccuracy);

        // 设定1毫秒定时器
        g_mmTimerId = timeSetEvent(1, 0, TimerProc, NULL, TIME_PERIODIC);
        if (g_mmTimerId == 0)
        {
            std::cout << "timeSetEvent failed: %d" << GetLastError() << std::endl;
            return FALSE;
        }

        return TRUE;
    }

    return FALSE;
}

int main(int argc, char** argv)
{
    WSADATA wsadata = { 0 };
    (void)WSAStartup(MAKEWORD(2, 2), &wsadata);
    InitUdpSock();
    InitHighTimer();
    (void)getchar();
    FreeHighTimer();
    ExitUdpSock();
    WSACleanup();
    return 0;
}


×
打赏作者
最新回复 (0)
只看楼主
全部楼主
返回