У меня есть приложение C++ с 2 потоками. Приложение отображает датчик на экране с индикатором, который вращается на основе угла, полученного через сокет UDP. Моя проблема заключается в том, что индикатор должен вращаться с постоянной скоростью, но он ведет себя, как время замедляет время от времени, а также быстро вперед, чтобы быстро догнать в другое время, с некоторыми паузами с перерывами.Производительность приложения C++ зависит от использования потоков
Каждый кадр, основная (основная) резьба защищает копию угла от нити UDP. Поток UDP также защищает запись общей переменной. Я использую объект Windows CriticalSection для защиты «связи» между потоками. Пакет UDP принимается примерно с той же скоростью, что и обновление дисплея. Я использую Windows 7, 64 бит, с 4-ядерным процессором.
Я использую отдельное приложение python для трансляции UDP-пакета. Я использую функцию python, time.sleep, чтобы поддерживать широковещательную передачу с постоянной скоростью.
Почему приложение замедляется? Почему приложение кажется ускоренным, вместо привязки к последнему углу? Какое правильное исправление?
EDIT: Я не уверен на 100%, что все значения угла запоминаются, когда приложение кажется «вперед». Приложение мгновенно привязывается к некоторому значению (не уверен, является ли оно «последним»).
EDIT 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();
}
Затруднились ответить, посмотрев фактический код. Однако это звучит так, будто что-то блокирует поток рендеринга. Я попытался охватить проблему, выполнив некоторые профилирования в ваших потоках. Также я прочитал, что вы получаете угол через UDP-сокет? Как вы достигаете постоянной скорости вращения? Я ожидаю, что битрейт не всегда будет постоянным. – Gio
Код темы и защитный код: http://pastebin.com/LrDcSQKS http://pastebin.com/MLDbNTjk – cyrf
укажите, пожалуйста, как можно больше кода в свой вопрос! – didierc