对于同一任务,线程的等待时间在 0 到 30000 微秒之间系统地切换

Waiting-time of thread switches systematicly between 0 and 30000 microseconds for the same task

本文关键字:30000 之间 系统地 任务 于同一 线程 等待时间      更新时间:2023-10-16

我正在编写一个小的控制台游戏引擎,为了获得更好的性能,我希望使用两个缓冲区使用 2 个线程(或更多,但此任务为 2 个(。一个线程在第一个缓冲区中绘制下一帧,而另一个线程正在从第二个缓冲区读取当前帧。然后交换缓冲区。

原因,我只能在两个线程都完成任务并且绘图/书写线程恰好是等待线程的情况下交换它们。但是它等待的时间或多或少地在两个值之间系统地切换,这里有一些我所做的消息(以微秒为单位(:

0, 36968, 0, 36260, 0, 35762, 0, 38069, 0, 36584,0, 36503

很明显,这不是巧合,但我无法弄清楚问题是什么,因为这是我第一次使用线程。

这里是代码,如果需要,请要求更多,我认为发布所有代码太多了:

头文件(管理器当前只添加一个指向我的 WinAppBase 类的指针(:

class SwapChain : Manager
{
WORD                            *pScreenBuffer1, *pScreenBuffer2, *pWritePtr, *pReadPtr, *pTemp;
bool                            isRunning, writingFinished, readingFinished, initialized;
std::mutex                      lockWriting, lockReading;
std::condition_variable         cvWriting, cvReading;
DWORD                           charsWritten;
COORD                           startPosition;
int                             screenBufferWidth;

// THREADS (USES NORMAL THREAD AS SECOND THREAD)
void ReadingThread();
// THIS FUNCTION IS ONLY FOR INTERN USE
void SwapBuffers();
public:
// USE THESE TO CONTROL WHEN THE BUFFERS GET SWAPPED
void BeginDraw();
void EndDraw();
// PUT PIXEL | INLINED FOR BETTER PERFORMANCE
inline void PutPixel(short xPos, short yPos, WORD color)
{
this->pWritePtr[(xPos * 2) + yPos * screenBufferWidth] = color;
this->pWritePtr[(xPos * 2) + yPos * screenBufferWidth + 1] = color;
}
// GENERAL CONTROL OVER SWAP CHAIN
void Initialize();
void Run();
void Stop();
// CONSTRUCTORS
SwapChain(WinAppBase * pAppBase);
virtual ~SwapChain();
};

CPP 文件

SwapChain::SwapChain(WinAppBase * pAppBase)
:
Manager(pAppBase)
{
this->isRunning             =   false;
this->initialized           =   false;
this->pReadPtr              =   NULL;
this->pScreenBuffer1        =   NULL;
this->pScreenBuffer2        =   NULL;
this->pWritePtr             =   NULL;
this->pTemp                 =   NULL;
this->charsWritten          =   0;
this->startPosition         =   { 0, 0 };
this->readingFinished       =   0;
this->writingFinished       =   0;
this->screenBufferWidth     =   this->pAppBase->screenBufferInfo.dwSize.X;
}
SwapChain::~SwapChain()
{
this->Stop();
if (_CrtIsValidHeapPointer(pReadPtr))
delete[] pReadPtr;
if (_CrtIsValidHeapPointer(pScreenBuffer1))
delete[] pScreenBuffer1;
if (_CrtIsValidHeapPointer(pScreenBuffer2))
delete[] pScreenBuffer2;
if (_CrtIsValidHeapPointer(pWritePtr))
delete[] pWritePtr;
}
void SwapChain::ReadingThread()
{
while (this->isRunning)
{
this->readingFinished = 0;
WriteConsoleOutputAttribute(
this->pAppBase->consoleCursor,
this->pReadPtr,
this->pAppBase->screenBufferSize,
this->startPosition,
&this->charsWritten
);
memset(this->pReadPtr, 0, this->pAppBase->screenBufferSize);
this->readingFinished = true;
this->cvWriting.notify_all();
if (!this->writingFinished)
{
std::unique_lock<std::mutex> lock(this->lockReading);
this->cvReading.wait(lock);
}
}
}
void SwapChain::SwapBuffers()
{
this->pTemp     =   this->pReadPtr;
this->pReadPtr  =   this->pWritePtr;
this->pWritePtr =   this->pTemp;
this->pTemp     =   NULL;
}
void SwapChain::BeginDraw()
{
this->writingFinished = false;
}
void SwapChain::EndDraw()
{
TimePoint tpx1, tpx2;
tpx1 = Clock::now();
if (!this->readingFinished)
{
std::unique_lock<std::mutex> lock2(this->lockWriting);
this->cvWriting.wait(lock2);
}
tpx2 = Clock::now();
POST_DEBUG_MESSAGE(std::chrono::duration_cast<std::chrono::microseconds>(tpx2 - tpx1).count(), "EndDraw wating time");
SwapBuffers();
this->writingFinished = true;
this->cvReading.notify_all();
}
void SwapChain::Initialize()
{
if (this->initialized)
{
POST_DEBUG_MESSAGE(Result::CUSTOM, "multiple initialization");
return;
}
this->pScreenBuffer1 = (WORD *)malloc(sizeof(WORD) * this->pAppBase->screenBufferSize);
this->pScreenBuffer2 = (WORD *)malloc(sizeof(WORD) * this->pAppBase->screenBufferSize);
for (int i = 0; i < this->pAppBase->screenBufferSize; i++)
{
this->pScreenBuffer1[i] = 0x0000;
}
for (int i = 0; i < this->pAppBase->screenBufferSize; i++)
{
this->pScreenBuffer2[i] = 0x0000;
}
this->pWritePtr = pScreenBuffer1;
this->pReadPtr = pScreenBuffer2;
this->initialized = true;
}
void SwapChain::Run()
{
this->isRunning = true;
std::thread t1(&SwapChain::ReadingThread, this);
t1.detach();
}
void SwapChain::Stop()
{
this->isRunning = false;
}

这是我运行 SwapChain 类的地方:

void Application::Run()
{
this->engine.graphicsmanager.swapChain.Initialize();
Sprite<16, 16> sprite(&this->engine);
sprite.LoadSprite("engine/resources/TestData.xml", "root.test.sprites.baum");
this->engine.graphicsmanager.swapChain.Run();
int a, b, c;
for (int i = 0; i < 60; i++)
{
this->engine.graphicsmanager.swapChain.BeginDraw();
for (c = 0; c < 20; c++)
{
for (a = 0; a < 19; a++)
{
for (b = 0; b < 10; b++)
{
sprite.Print(a * 16, b * 16);
}
}
}
this->engine.graphicsmanager.swapChain.EndDraw();
}
this->engine.graphicsmanager.swapChain.Stop();

_getch();
}

上面的 for 循环只是将精灵从控制台的左上角绘制 20 次到右下角 - 在此期间缓冲区不会被交换,并且再次交换总共 60 次(因此缓冲区被交换 60 次(。 精灵。Print 使用 SwapChain 的 PutPixel 函数。

这里是WinAppBase(它或多或少由类似全局的变量组成(

class WinAppBase
{
public:
// SCREENBUFFER
CONSOLE_SCREEN_BUFFER_INFO      screenBufferInfo;
long                            screenBufferSize;
// CONSOLE
DWORD                           consoleMode;
HWND                            consoleWindow;
HANDLE                          consoleCursor;
HANDLE                          consoleInputHandle;
HANDLE                          consoleHandle;
CONSOLE_CURSOR_INFO             consoleCursorInfo;
RECT                            consoleRect;
COORD                           consoleSize;
// FONT
CONSOLE_FONT_INFOEX             fontInfo;
// MEMORY
char *                          pUserAccessDataPath;

public:
void reload();
WinAppBase();
virtual ~WinAppBase();
};

没有错误,只是这个交替等待的时间。 也许您想先看看我是否正确地同步了线程?我不确定如何使用互斥锁或条件变量,所以它可能来自那里。

除此之外,它工作正常,精灵按原样显示。

您使用的时钟的分辨率可能有限。下面是 Microsoft 提供的分辨率为 15 毫秒(15000 微秒(的时钟的随机示例:为什么 .NET 计时器的分辨率限制为 15 毫秒?

如果一个线程经常等待另一个线程,则完全有可能(假设上述时钟分辨率(它有时等待两个时钟周期,有时不等待。也许您的时钟只有 30 毫秒的分辨率。我们真的无法从代码中分辨出来。使用此时钟,您是否在其他地方获得更精确的测量结果?

还有其他系统在起作用,例如操作系统调度程序或任何控制您的std::thread的系统。这个(希望(更精细,但所有这些交互如何发挥作用并不一定是明显或直观的。