khan |
Отправлено: 13.02.2007, 01:31 |
|
Ученик-кочегар
Группа: Участник
Сообщений: 19
|
Задачка такая: один поток очень быстро получает данные из компорта парсит их, другой поток должен выводить все данные на график. Т.к. данные идут слишком быстро выводящий на график поток не успевает их все отрисовать часть пропадает. Поэтому решено запихивать данные в очередь и затем в другом потоке читать эту очередь и выводить на график не по одной точке, а все что есть в буфере.
Вопрос в том как создать потокобезопасную очередь? |
|
olegenty |
Отправлено: 13.02.2007, 08:23 |
|
Ветеран
Группа: Модератор
Сообщений: 2412
|
например так:
// пишем заголовчник, определяем шаблон локера (директивы компилятору можешь вытереть, они у меня для определённости вставлены во все заголовочники всех проектов):
CODE |
#ifndef ObjectLevelLockH
#define ObjectLevelLockH
#pragma option push -Ve -b -a4
#include <sysutils.hpp>
#include <map.h>
#include <STLPort\memory>
template <class Host> class OInstanceLocker
{
auto_ptr<TMultiReadExclusiveWriteSynchronizer> pmtx;
public:
__fastcall OInstanceLocker()
{
pmtx = auto_ptr<TMultiReadExclusiveWriteSynchronizer>(new TMultiReadExclusiveWriteSynchronizer());
}
__fastcall ~OInstanceLocker()
{
}
class Read;
friend class Read;
class Read
{
OInstanceLocker& host_;
Read(const Read&);
Read& operator=(const Read&);
public:
__fastcall Read(Host& host) : host_(host)
{
host_.pmtx->BeginRead();
}
__fastcall ~Read()
{
host_.pmtx->EndRead();
}
};
class Write;
friend class Write;
class Write
{
OInstanceLocker& host_;
Write(const Write&);
Write& operator=(const Write&);
public:
Write(Host& host) : host_(host)
{
host_.pmtx->BeginWrite();
}
~Write()
{
host_.pmtx->EndWrite();
}
};
};
#pragma option pop
#endif
|
потом используем локер (на основе примера пиши своё)
*.h
CODE |
class PACKAGE CModule: public OInstanceLocker<CModule>
{
friend class CModules;
public:
CModule(AnsiString FilePath, AdapterType* const Adapter, PModuleDeletionCallback DeleteModule);
AnsiString FilePath();
AnsiString FileName();
HINSTANCE Handle();
CFileVersion Version();
CFields Functions();
private:
AnsiString FilePath_ ;
AnsiString FileName_ ;
HINSTANCE Handle_ ;
CFileVersion Version_ ;
PCounterFunc AddRef_ ;
PCounterFunc Release_ ;
PSpecFunc Functions_;
PReg Register_ ;
PReg Unregister_;
PModuleDeletionCallback DeleteModule_;
AdapterType* const Adapter_;
long AddRef();
long Release();
~CModule();
};
|
*.cpp
CODE |
CModule::CModule( AnsiString FilePath
, AdapterType* const Adapter
, PModuleDeletionCallback DeleteModule
)
: FilePath_(FilePath)
, FileName_(ExtractFilePath(FilePath))
, Version_(CFileVersion(FilePath))
, Handle_(NULL)
, AddRef_(NULL)
, Release_(NULL)
, DeleteModule_(DeleteModule)
, Adapter_(Adapter)
{
Handle_ = LoadLibrary(FilePath_.c_str());
if (Handle_)
{
AddRef_ = (PCounterFunc)GetProcAddress(Handle_, "AddRef");
Release_ = (PCounterFunc)GetProcAddress(Handle_, "Release");
Functions_ = (PSpecFunc)GetProcAddress (Handle_, "Functions");
Register_ = (PReg)GetProcAddress (Handle_, "Register");
Unregister_ = (PReg)GetProcAddress (Handle_, "Unregister");
if (!AddRef_ ) ErrorMessage("Ошибка загрузки модуля \"" + FilePath + "\": не найдена служебная функция AddRef, рекомендуется завершить работу и обратиться к разработчику");
if (!Release_ ) ErrorMessage("Ошибка загрузки модуля \"" + FilePath + "\": не найдена служебная функция Release, рекомендуется завершить работу и обратиться к разработчику");
if (!Functions_) ErrorMessage("Ошибка загрузки модуля \"" + FilePath + "\": не найдена служебная функция Functions, рекомендуется завершить работу и обратиться к разработчику");
if (Register_) Register_(Adapter_);
}
}
long CModule::AddRef()
{
Write write(*this); // запираем экземпляр класса на запись
if (AddRef_) return AddRef_();
else return 1;
}
long CModule::Release()
{
long RefCount = 0;
if (Release_) RefCount = Release_();
if (!RefCount)
{
delete this;
return 0;
}
return RefCount;
}
CModule::~CModule()
{
Write write(*this);
if (DeleteModule_) DeleteModule_(FilePath_);
if (Unregister_) Unregister_(Adapter_);
FreeLibrary(Handle_);
}
AnsiString CModule::FilePath()
{
Read read(*this); // запираем экземпляр класса на чтение
return FilePath_;
}
AnsiString CModule::FileName()
{
Read read(*this);
return FileName_;
}
HINSTANCE CModule::Handle()
{
Read read(*this);
return Handle_;
}
CFileVersion CModule::Version()
{
Read read(*this);
return Version_;
}
CFields CModule::Functions()
{
Read read(*this);
return Functions_();
}
|
|
|
khan |
Отправлено: 13.02.2007, 10:16 |
|
Ученик-кочегар
Группа: Участник
Сообщений: 19
|
Спасибо за внимание к данной проблеме! Однако мне кажется все должно быть гораздо проще. Что типа выставления флага во время записи данных в очередь из одного потока и проверка данного флага из другого? Или я не вижу каких-то еще проблем? |
|
olegenty |
Отправлено: 13.02.2007, 10:49 |
|
Ветеран
Группа: Модератор
Сообщений: 2412
|
угу, не видишь.
представь себе, что один поток уже начал выставлять флаг, недовыставил, и значение флага пытается прочитать/записать другой поток... (один пару тактов поработал, потом другой — пару тактов и т.д.)
"флагом" может быть Event (вариант раз), данные можно обернуть в критическую секцию (вариант 2).
вариантов море. предложенный — ничто иное, как предложение Александреску в его произведении "Modern C++ Design". лично мне это решение показалось почти идеальным. можешь выбрать любое другое. рекомендую почитать Джефри Рихтера "Создание эффективных WIN32-приложений с учетом специфики 64-разрядной версии Windows".
|
|
khan |
Отправлено: 13.02.2007, 11:37 |
|
Ученик-кочегар
Группа: Участник
Сообщений: 19
|
Да, я наверно не правильно выразился... Флаг действительно Event. Но есть интересный момент. Что если читать не весь буфер, и всегда оставлять один элемент в нем. Читать соответственно с начала буфера до предпоследнего элемента. Может ли в такой ситуации возникнуть конфликт? |
|
olegenty |
Отправлено: 13.02.2007, 11:40 |
|
Ветеран
Группа: Модератор
Сообщений: 2412
|
может.
если хочешь по-простому — воспользуйся TThreadList вместо очереди. это потокобезопасный список. можешь и исходники посмотреть (как это реализовано). глядя на реализацию увидишь, что я предлагаю то же самое.
|
|
khan |
Отправлено: 13.02.2007, 11:48 |
|
Ученик-кочегар
Группа: Участник
Сообщений: 19
|
Спасибо буду разбираться. |
|
ion |
Отправлено: 13.02.2007, 14:43 |
|
Ученик-кочегар
Группа: Участник
Сообщений: 15
|
Извечная тема, скорее надо поменять сам принцип получения, если график не успевает за портом, тут уж ничего не поделаешь, график никогда не успеет,( П.Зенона), если есть промежутки между передачами у девайса
то что мешает синхронизоровать прием и считку буфера.
а вообще, я в таких случаЯх делал синхронизацию,( компьютер ведущий-
порт ведомый) в реальном времени, только взаместо родного таймера, использовал таймер мультимедиа, он более реален на малых величинах
и получал реальную рартинку, без буфера, даже писал осцилограф
на такой схеме, что вы предлогаете,можно, но уж больно геморойно,
( не знаю, на каких задачах вы работаете) |
|
khan |
Отправлено: 13.02.2007, 15:19 |
|
Ученик-кочегар
Группа: Участник
Сообщений: 19
|
Учитывая что скорость обмена данными 38400, то максимум можно слать 30 раз в секунду. Мы сделали 20, поток парсит пакеты достаточно быстро. Синхронизацию сделал используя критические секции. Однако ТChart по ходу тормозит весь процесс. Решил график рисовать вручную просто используя Image->Canvas. Тут возникла проблемка со сдвигом графика (затирается пиксель). Об этом написал в этом посте:
бегущий график |
|