C++ Builder
| Главная | Уроки | Статьи | FAQ | Форум | Downloads | Литература | Ссылки | RXLib | Диски |

 
queue из двух потоков, один поток пишет в queue другой читает
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. Тут возникла проблемка со сдвигом графика (затирается пиксель). Об этом написал в этом посте:
бегущий график

Вернуться в Вопросы программирования в C++Builder