我有一个带2个线程的C ++应用程序。该应用程序在屏幕上显示一个仪表,其指示器根据通过UDP套接字接收的角度旋转。我的问题是指标应该以恒定的速度旋转,但它的表现有时候会慢慢减速,而且它也可以快速前进,以便在其他时间快速赶上,间歇性地暂停一些。
每个帧,显示(主)线程保护UDP线程的角度副本。 UDP线程还保护写入共享变量。我使用Windows CriticalSection对象来保护'通信'线程之间。以与显示更新大致相同的速率接收UDP分组。我使用的是Windows 7,64位,带有4核处理器。
我使用单独的python应用程序来广播UDP数据包。我使用python函数time.sleep来保持广播的恒定速率。
为什么应用程序会变慢? 为什么应用程序似乎快进而不是捕捉到最新的角度? 什么是正确的解决方案?
编辑:当应用程序似乎“快进”时,我并不是100%确定所有角度值都会被记住。该应用程序有时会抢占一些价值(不确定它是否是最新的#39;)。
编辑2 :每个请求,一些代码。
void App::udp_update(DWORD thread_id)
{
Packet p;
_socket.recv(p); // edit: blocks until transmission is received
{
Locker lock(_cs);
_packet = p;
}
}
void App::main_update()
{
float angle_copy = 0.f;
{
Locker lock(_cs);
angle_copy = _packet.angle;
}
draw(angle_copy); // edit: blocks until monitor refreshes
}
Thread.h
class CS
{
private:
friend Locker;
CRITICAL_SECTION _handle;
void _lock();
void _unlock();
// not implemented by design
CS(CS&);
CS& operator=(const CS&);
public:
CS();
~CS();
};
class Locker
{
private:
CS& _cs;
// not implemented by design
Locker();
Locker(const Locker&);
Locker& operator=(const Locker&);
public:
Locker(CS& c)
: _cs(c)
{
_cs._lock();
}
~Locker()
{
_cs._unlock();
}
};
class Win32ThreadPolicy
{
public:
typedef Functor<void,TYPELIST_1(DWORD)> Callback;
private:
Callback _callback;
//SECURITY_DESCRIPTOR _sec_descr;
//SECURITY_ATTRIBUTES _sec_attrib;
HANDLE _handle;
//DWORD _exitValue;
#ifdef USE_BEGIN_API
unsigned int _id;
#else // USE_BEGIN_API
DWORD _id;
#endif // USE_BEGIN_API
/*volatile*/ bool _is_joined;
#ifdef USE_BEGIN_API
static unsigned int WINAPI ThreadProc( void* lpParameter );
#else // USE_BEGIN_API
static DWORD WINAPI ThreadProc( LPVOID lpParameter );
#endif // USE_BEGIN_API
DWORD _run();
void _join();
// not implemented by design
Win32ThreadPolicy();
Win32ThreadPolicy(const Win32ThreadPolicy&);
Win32ThreadPolicy& operator=(const Win32ThreadPolicy&);
public:
Win32ThreadPolicy(Callback& func);
~Win32ThreadPolicy();
void Spawn();
void Join();
};
/// helps to manage parallel operations.
/// attempts to mimic the C++11 std::thread interface, but also passes the thread ID.
class Thread
{
public:
typedef Functor<void,TYPELIST_1(DWORD)> Callback;
typedef Win32ThreadPolicy PlatformPolicy;
private:
PlatformPolicy _platform;
/// not implemented by design
Thread();
Thread(const Thread&);
Thread& operator=(const Thread&);
public:
/// begins parallel execution of the parameter, func.
/// \param func, the function object to be executed.
Thread(Callback& func)
: _platform(func)
{
_platform.Spawn();
}
/// stops parallel execution and joins with main thread.
~Thread()
{
_platform.Join();
}
};
Thread.cpp
#include "Thread.h"
void CS::_lock()
{
::EnterCriticalSection( &_handle );
}
void CS::_unlock()
{
::LeaveCriticalSection( &_handle );
}
CS::CS()
: _handle()
{
::memset( &_handle, 0, sizeof(CRITICAL_SECTION) );
::InitializeCriticalSection( &_handle );
}
CS::~CS()
{
::DeleteCriticalSection( &_handle );
}
Win32ThreadPolicy::Win32ThreadPolicy(Callback& func)
: _handle(NULL)
//, _sec_descr()
//, _sec_attrib()
, _id(0)
, _is_joined(true)
, _callback(func)
{
}
void Win32ThreadPolicy::Spawn()
{
// for an example of managing descriptors, see:
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa446595%28v=vs.85%29.aspx
//BOOL success_descr = ::InitializeSecurityDescriptor( &_sec_descr, SECURITY_DESCRIPTOR_REVISION );
//TODO: do we want to start with CREATE_SUSPENDED ?
// TODO: wrap this with exception handling
#ifdef USE_BEGIN_END
// http://msdn.microsoft.com/en-us/library/kdzttdcb%28v=vs.100%29.aspx
_handle = (HANDLE) _beginthreadex( NULL, 0, &Thread::ThreadProc, this, 0, &_id );
#else // USE_BEGIN_END
_handle = ::CreateThread( NULL, 0, &Win32ThreadPolicy::ThreadProc, this, 0, &_id );
#endif // USE_BEGIN_END
}
void Win32ThreadPolicy::_join()
{
// signal that the thread should complete
_is_joined = true;
// maybe ::WFSO is not the best solution.
// "Except that WaitForSingleObject and its big brother WaitForMultipleObjects are dangerous.
// The basic problem is that these calls can cause deadlocks,
// if you ever call them from a thread that has its own message loop and windows."
// http://marc.durdin.net/2012/08/waitforsingleobject-why-you-should-never-use-it/
//
// He advises to use MsgWaitForMultipleObjects instead:
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms684242%28v=vs.85%29.aspx
DWORD result = ::WaitForSingleObject( _handle, INFINITE );
// _handle must have THREAD_QUERY_INFORMATION security access enabled to use the following:
//DWORD exitCode = 0;
//BOOL success = ::GetExitCodeThread( _handle, &_exitValue );
}
Win32ThreadPolicy::~Win32ThreadPolicy()
{
}
void Win32ThreadPolicy::Join()
{
if( !_is_joined )
{
_join();
}
// this example shows that it is correct to pass the handle returned by CreateThread
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms682516%28v=vs.85%29.aspx
::CloseHandle( _handle );
_handle = NULL;
}
DWORD Win32ThreadPolicy::_run()
{
// TODO: do we need to make sure _id has been assigned?
while( !_is_joined )
{
_callback(_id);
::Sleep(0);
}
// TODO: what should we return?
return 0;
}
#ifdef USE_BEGIN_END
unsigned int WINAPI Thread::ThreadProc( LPVOID lpParameter )
#else // USE_BEGIN_END
DWORD WINAPI Win32ThreadPolicy::ThreadProc( LPVOID lpParameter )
#endif // USE_BEGIN_END
{
Win32ThreadPolicy* tptr = static_cast<Win32ThreadPolicy*>( lpParameter );
tptr->_is_joined = false;
// when this function (ThreadProc) returns, ::ExitThread is used to terminate the thread with an "implicit" call.
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms682453%28v=vs.85%29.aspx
return tptr->_run();
}
答案 0 :(得分:2)
我知道这在假设空间中有点但是:
您所谈论的费率是在&#34;服务器&#34;中设置的。和&#34;客户&#34;通过睡眠来控制数据包的发送速度。这不一定是实际传输速率,因为操作系统可以非常不对称的方式安排您的流程(时间)。
这可能意味着当服务器获得更多时间时,它将用数据包填充OS缓冲区(客户端将获得更少的处理器时间,因此,以较低的速率消耗=&gt;减慢仪表速度)。然后,当客户端获得服务器的更多时间时,它将快速消耗所有数据包,而更新线程仍将进行一些等待。但这并不意味着它会“快速”,因为你使用一个关键部分来锁定数据包更新,所以你可能不会从OS缓冲区中消耗太多的包直到新的更新。 (你可能有一个&#34;对着&#34;但是只有一小步)。我基于这样一个事实:我看到你的接收或更新方法没有实际睡眠(唯一的睡眠是在服务器端完成的)。