Модераторы: Daevaorn
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Очередь и паралельный доступ к элементам, помогите найти ошибку если есть 
:(
    Опции темы
neosapient
Дата 27.10.2007, 01:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 672
Регистрация: 16.8.2006

Репутация: нет
Всего: 4



Здраствуйте.

Понимаю, что пост длинный, но постарайтесь дочитать до конца - тут есть над чем призадуматься.

Есть однонаправленый список. Один поток добавляет элементы в список. Ещё два потока удаляют элементы из списка - первый через 2 секунды, второй случайным образом "ассинхронно угадывает", какой элемент удалить.

Очередь в некотором смысле FIFO. То есть новые элементы добавляются в голову. А обработка начинается с головы (с самых молоды) и заканчивается хвостом (давно стоящие в очереди), следовательно они могут успеть созреть.

Есть класс диспетчер, который управляет доступом к спискам. Управление происходит через два объекта синхронизации. Один из них - m_sync_act - более "сильный" и отвечает за обработку элементов списка. Второй - m_sync_add - отвечает за переключени флага m_no_act. Если список не обрабатывается потоками удаления, то флаг выставлен в true, а новый элемент добавляется в голову очереди. Если список  обрабатывается потоками удаления, то флаг выставлен в false, а новый элемент добавляется в голову вторичной очереди. Когда поток удаления будет освобождать доступ к основной очереди, то вторичная очередь добавиться в голову первичной.

И ещё, щас код переписал на коленке по быстрому из основного рабочего в упрощеном варианте, , так что за синтаксические ошибки извените. (Прошу прощенья, за выходные поправлю, если есть ляпы, но помощь нужна скорее, чем я дооформлю тему)
Проблему, из-за которой начал эту тему, можно назвать так: элементы в список попадают, но не обрабатываются (не выходят). Успешной обработкой я называю в данном случае печать на экран и удаление из списка.
Проверьте есть ли идеологические ошибки в моем коде  smile 

Объект сихрониза
Код

// TSync.h
#pragma once
//+------------------------------------------------------------------+
//| Mutex synchronizer                                               |
//+------------------------------------------------------------------+
class TSync
  {
private:
   HANDLE hMutex;

public:
                     TSync()  { hMutex = CreateMutex(NULL,FALSE,"MutexToProtectListDispatcherThread"); }
                    ~TSync()  { CloseHandle(hMutex); hMutex=NULL; }
   inline void       Lock()   { WaitForSingleObject(hMutex,INFINITE); }
   inline void       Unlock() { ReleaseMutex(hMutex); }
  };


Описание диспетчера задач
Код

// TListDispatcher.h

#pragma once
#include "TSync.h"

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
struct ListItem
  {
   int               num;
   DWORD             localtime;
   DWORD             timeout;
   ListItem         *next;
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class TListDispatcher
  {
public:
   ListItem         *m_head;
   ListItem         *m_add_first;
   ListItem         *m_add_end;
   ListItem         *m_act_before;
   ListItem         *m_act_item;
   int               m_min_time_of_delay;
   int               m_timeout;
private:
   HANDLE            m_thread;
   bool              m_stopflag;
public:
   volatile bool     m_no_act;             // флаг синхронизации 
   TSync             m_sync_act;           // синхрозитор обработки переменных
   TSync             m_sync_add;           // синхрозитор добавления переменных
public:
                     TListDispatcher();
                    ~TListDispatcher();
//----
   int               Initialize(void);
   int               Shutdown(void);
//----
   int               Add(const int num);
   ListItem*         GetNext(void);
   ListItem*         DeleteThisAndGetNext(void);
   int               Locking(void);
   int               Unlocking(void);
//----
   void              CheckTick(const int number);
private:
//----
   void              Process(void);

//----
   static UINT __stdcall ThreadFuntion(LPVOID pParam);
  };
//+------------------------------------------------------------------+
extern TListDispatcher ExtListDisp;




Методы класса
Код

// TListDispatcher.cpp

#include "TListDispatcher.h"

//----
TListDispatcher ExtListDisp;
//+------------------------------------------------------------------+
//| конструктор по умолчанию                                         |
//+------------------------------------------------------------------+
TListDispatcher::TListDispatcher() : m_head(NULL),m_stopflag(TRUE),
                                     m_thread(NULL),m_no_act(TRUE),
                                     m_add_first(NULL),m_add_end(NULL),
                                     m_act_before(NULL),m_act_item(NULL),
                                     m_min_time_of_delay(0),m_timeout(2)
  {
//----
  }
//+------------------------------------------------------------------+
//| деструктор по умолчанию                                          |
//+------------------------------------------------------------------+
TListDispatcher::~TListDispatcher()
  {
   ListItem * item;
//---- убъем поток обработки
   Shutdown();
//---- удаляем данные, но надо бы залочиться
   m_sync_act.Lock();
   m_sync_add.Lock();
//---- удалим их
   while(m_head!=NULL) { 
       item=m_head->next; 
       delete m_head; 
       m_head=item; }
   while(m_add_first!=NULL) { 
       item=m_add_first->next; 
       delete m_add_first; 
       m_add_first=item; }
//----
   m_head = NULL;
   m_stopflag = TRUE;
   m_thread = NULL;
   m_no_act = TRUE;
   m_add_first = NULL;
   m_add_end = NULL;
   m_act_before = NULL;
   m_act_item = NULL;
//----
   m_sync_add.Unlock();
   m_sync_act.Unlock();
  }
//+------------------------------------------------------------------+
//| запуск потока обработки                                          |
//+------------------------------------------------------------------+
int TListDispatcher::Initialize(void)
  {
   UINT tid;
//---- закрываем, если что-то осталось
   Shutdown();
//---- сбрасываем флаг остановки
   m_stopflag=FALSE;
//---- запускаем поток обработки
   m_thread=(HANDLE)_beginthreadex(NULL,0,ThreadFuntion,(void*)this,0,&tid);
//----
   m_min_time_of_delay = 2;
   m_timeout = 2;
//----
   return(TRUE);
  }
//+------------------------------------------------------------------+
//| Завершение рабочего потока                                       |
//+------------------------------------------------------------------+
int TListDispatcher::Shutdown(void)
  {
//---- сначала затормозим все
   if(m_thread!=NULL)
     {
      //---- говорим пора остановиться
      m_stopflag=TRUE;
      //---- ждем пока объект не освободиться
      WaitForSingleObject(m_thread,INFINITE);
      //---- принудительно убиваем (не уверен, что теперь это обязательное действие, но надеюсь не повредит)
      CloseHandle(m_thread);
      m_thread=NULL;
     }
//----
   return(TRUE);
  }
//+------------------------------------------------------------------+
//| Добавление нового элемента для обработки                         |
//+------------------------------------------------------------------+
int TListDispatcher::Add(const int number)
  {
//----
   ListItem* item;
   if(num<1) return(FALSE);
//----
   m_sync_add.Lock();
//---- копируем указатель на начало очереди
   if(m_no_act==true){// если действий сейчас не производиться
       item = m_head;
//---- выделим новый элемент
       if((m_head=new ListItem)==NULL)
        {
          //---- возвращаем старый указатель на начало списка
          m_head = item;
          m_sync_add.Unlock();
          //---- скажем что памяти нету
          printf("Not enough memory");
          //----
          return(FALSE);
         }
       m_head->next = item;
   }
   else{
       item = m_add_first;
//---- выделим новый элемент
       if((m_add_first=new ListItem)==NULL)
        {
          //---- возвращаем старый указатель на начало списка
          m_add_first = item;
          m_sync_add.Unlock();
          //---- скажем что памяти нету
          printf("Not enough memory");
          //----
          return(FALSE);
         }
       m_add_first->next = item;
       if(m_add_end==NULL)m_add_end = m_add_first;
   }
//---- заполним поля нового элемента
   m_head->timeout = m_head->tradetime = time(NULL);
   m_head->num = number;
//---- освобождаем синхронизацию
   m_sync_add.Unlock();
   return(TRUE);
  }
//+------------------------------------------------------------------+
//| Взять элемент                                                    |
//+------------------------------------------------------------------+
ListItem* TListDispatcher::GetNext(void)
{
    if(m_act_item==NULL){
        m_act_item = m_head;
        m_act_before = NULL;
    }
    else{
        m_act_before = m_act_item;
        m_act_item = m_act_item->next;
    }
    return m_act_item;
}
//+------------------------------------------------------------------+
//| Взять элемент и удалить                                          |
//+------------------------------------------------------------------+
ListItem* TListDispatcher::DeleteThisAndGetNext(void)
{
    ListItem* del;
    del = m_act_item;
    if(m_act_item==m_head){
        m_act_before = NULL;
        m_act_item = m_head = m_act_item->next;
    }
    else{
        if(m_act_before!=NULL) m_act_before->next = m_act_item->next;
        m_act_item = m_act_item->next;
    }
    delete del;
    return m_act_item;
}
//+------------------------------------------------------------------+
//| Закрытие списка для добавления, пусть пользуються вторичным      |
//+------------------------------------------------------------------+
int TListDispatcher::Locking(void)
{
    m_sync_act.Lock();
    m_sync_add.Lock();
    m_no_act = false;
    m_act_before = NULL;
    m_act_item = NULL;
    m_sync_add.Unlock();
    return TRUE;
}
//+------------------------------------------------------------------+
//| Открытие списка для добавления, перемещаем данные из  вторичного |
//+------------------------------------------------------------------+
int TListDispatcher::Unlocking(void)
{
    m_sync_add.Lock();
    m_no_act = true;
    if(m_add_end!=NULL){// значит в втором списке хотя бы один элемент
        m_add_end->next = m_head;
        m_head = m_add_first;
        m_add_first = NULL;
        m_add_end = NULL;
    }
    m_act_before = NULL;
    m_act_item = NULL;
    m_sync_add.Unlock();
    m_sync_act.Unlock();
    return TRUE;
}
//+------------------------------------------------------------------+
//| Обработка элементов по истечении срока                           |
//+------------------------------------------------------------------+
void TListDispatcher::Process(void)
  {
   ListItem *item;
   time_t BeginTime;
//---- крутимся пока не скажут выходить
   while(m_stopflag==FALSE)
     {
      BeginTime=ExtServer->TradeTime();
      //---- локируемся
      Locking();
      //---- берем верхушку
      item=GetNext();
      //---- крутимся пока не скажут выходить или не кончиться список
      while((m_stopflag==FALSE)&&(item!=NULL))
        {
         if(m_min_time_of_delay>0){
            if((item->tradetime+ExtProcessor.m_min_time_of_delay)<BeginTime){
               if(ExtProcessor.m_timeout>0)
                  if((item->timeout + ExtProcessor.m_timeout*60) < BeginTime){
                     //---- данные у нас надо хранить как зеницу ока
                     printf("timeout %i\n",item->num);
                     //---- освобождаем текущий элемент
                     item = DeleteThisAndGetNext();
                     continue;
                  }
               //---- обрабатываем текущий элемент
               printf("%i\n",item->num);
               //---- освобождаем текущий элемент
               item = DeleteThisAndGetNext();
            }
            else{
               item = GetNext();
            }
         }
         else{
             //---- обрабатываем текущий элемент
               printf("%i\n",item->num);
            //---- освобождаем текущий элемент
            item = DeleteThisAndGetNext();
         }
        }
      Unlocking();
      if(m_stopflag==FALSE)Sleep(100);//Sleep(500);
     }
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void TListDispatcher::CheckTick(const int number)
{
   ListItem        *item;
   if(number<1) return;
   Locking();
   item=GetNext();
   while(item!=NULL){
      if(item->num == number){
             //---- обрабатываем текущий элемент
            printf("checktick %i\n",item->num);
            //---- освобождаем текущий элемент
            item = DeleteThisAndGetNext();
         }
      }
      else{
         item=GetNext();
      }
   }
   Unlocking();
}

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
UINT __stdcall TListDispatcher::ThreadFuntion(LPVOID pParam)
  {
//---- передадим управление фукнции обработки
   if(pParam!=NULL) ((TMarketDispatcher *)pParam)->Process();
//---- завершаем поток
   _endthreadex(0);
   return(0);
  }



И использование данного класса. Естественно циклы только в нижнем примере бесконечные (здесь только для простоты иллюстрации) - while(true) -, в жизне там стоит флаг завершения обработки, как это было сделано в выше приведенном классе.


Код

#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
#include <process.h>

#include "TListDispatcher.h"


UINT __stdcall ThreadCheck(LPVOID pParam)
{
while(true){
    //---- передадим управление фукнции обработки
    ExtListDisp.CheckTick(rand(100));
    Sleep(100);
}
//---- завершаем поток
_endthreadex(0);
return(0);
}

void main(void)
{
int a=0;
ExtListDisp.Initialize();
_beginthreadex(NULL,0,ThreadCheck,(void*)this,0,0);
while(true){
    a++;
    a=a%100;
    ExtListDisp.Add(a);
    Sleep(10);
}
ExtListDisp.Shutdown();
}



Это сообщение отредактировал(а) neosapient - 28.10.2007, 01:01

Присоединённый файл ( Кол-во скачиваний: 3 )
Присоединённый файл  ListDisp.rar 3,54 Kb
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0961 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.