1 Востаннє редагувалося Bartash (29.12.2014 13:46:12)

Тема: Потокозахищені альтернативи функцій у time.h

Доброго часу доби на Форумі.

Працюю зараз над проектом, у якому необхідно працювати з датою та часом у різних інтерпретаціях: як TIMESTAMP-величиною та як структурою відповідних полів (рік, місяць і т.д). По суті, можна було б обійтися використанням стандартного time_t для TIMESTAMP та структурою tm, які взаємоконвертуються функціями localtime() та mktime() достатньо точно для мого випадку.

Але... Функція localtime() псує всю малину, оскільки є потоконебезпечною (використовує статичну змінну у якості структури, яку повертає), а мій проект передбачає одночасну роботу кількох потоків, які здійснюють взаємоконвертування дати й часу досить часто.

Варіанти localtime_r(), localtime_s() не підходять у зв'язку з використанням MinGW.

Чи є альтернативний шлях, крім як писати власний потокозахищений декоратор для роботи з датою та часом?

I belong to the Dead Generation.
Подякували: leofun011

2

Re: Потокозахищені альтернативи функцій у time.h

Можна семафор використати для захисту одночасного виклику localtime().
Чи це ви і маєте на увазі під власний потокозахищений декоратор для роботи з датою та часом?

Подякували: Bartash1

3

Re: Потокозахищені альтернативи функцій у time.h

Replace написав:

Можна семафор використати для захисту одночасного виклику localtime().
Чи це ви і маєте на увазі під власний потокозахищений декоратор для роботи з датою та часом?

Це і мав на увазі. Клас-декоратор. Тільки я про мьютекс думав, але суті не змінює. Досі не розумію, для чого було так реалізовувати функцію - через статичну змінну...%_%

Власне, від ідеї синхронізації localtime() радості мало, бо операція має здійснюватися досить часто: мова іде про синтаксичний аналізатор тексту, де постійно фігурують дані дати/часу. З QDateTime таких проблем не було, але зараз задача - мінімалістичність та швидкодія, тож Qt відпадає...

I belong to the Dead Generation.
Подякували: leofun011

4 Востаннє редагувалося Bartash (29.01.2013 22:56:24)

Re: Потокозахищені альтернативи функцій у time.h

Викладу свій варіант декоратора, бо знайти зручну альтернативу не вдалося. Може, комусь знадобиться.
Інтерфейс та можливості класу адаптовані до вимог проекту - не факт, що такий шлях є оптимальним сам по собі.

Основні відмінності декоратора від патріархальної tm-структури:
1. Рік задається від Різдва Христового, себто 2012 рік у datetime еквівалентний 112 для tm-структури.
2. Місяці нумеруються з 1 до 12, що робить клас орієнтованим на людські, а не машинні, потреби.

Прихований текст

/* datetime.h */
#ifndef DATETIME_H_
#define DATETIME_H_

#include <ctime>
#include <windows.h>

#define DATETIME_START    1900

namespace DateTime
{
    class datetime
    {
    private:
        time_t timestamp;
    public:
        datetime();
        explicit datetime(time_t ts);
        explicit datetime(int year, int month, int day, int hour, int minute, int second);

        ~datetime();

        bool operator == (const datetime &dt)const;
        bool operator != (const datetime &dt)const;
        bool operator > (const datetime &dt)const;
        bool operator < (const datetime &dt)const;
        bool operator >= (const datetime &dt)const;
        bool operator <= (const datetime &dt)const;

        operator time_t()const;
    protected:
        inline void makeTM(tm &dest)const;
    public:
        int year()const;
        int month()const;
        int day()const;
        int hour()const;
        int minute()const;
        int second()const;
    };
}
#endif /* DATETIME_H_ */
/* datetime.cpp */
#include "datetime.h"
using namespace DateTime;

    static HANDLE dtMutex = CreateMutex(0, false, 0);

    datetime::datetime(): timestamp(0)
    {}

    datetime::datetime(time_t ts): timestamp(ts)
    {}

    datetime::datetime(int year, int month, int day, int hour, int minute, int second)
    {
        register tm tmp;
        tmp.tm_year = year-DATETIME_START;
        tmp.tm_mon = month-1;
        tmp.tm_mday = day;
        tmp.tm_hour = hour;
        tmp.tm_min = minute;
        tmp.tm_sec = second;

        this->timestamp = mktime(&tmp);
    }

    datetime::~datetime()
    {}

    bool datetime::operator == (const datetime &dt)const
    {
        return (this->timestamp == dt.timestamp);
    }

    bool datetime::operator != (const datetime &dt)const
    {
        return !(*this == dt);
    }

    bool datetime::operator > (const datetime &dt)const
    {
        return (this->timestamp > dt.timestamp);
    }

    bool datetime::operator < (const datetime &dt)const
    {
        return !(*this > dt);
    }

    bool datetime::operator >= (const datetime &dt)const
    {
        return (this->timestamp >= dt.timestamp);
    }

    bool datetime::operator <= (const datetime &dt)const
    {
        return !(*this >= dt);
    }

    datetime::operator time_t()const
    {
        return this->timestamp;
    }

    void datetime::makeTM(tm &dest)const
    {
        if( WaitForSingleObject(dtMutex, INFINITE) == WAIT_OBJECT_0 )
        {
            tm *data = localtime(&timestamp);
            dest = *data;

            ReleaseMutex(dtMutex);
        }
    }

    int datetime::year()const
    {
        tm dest;
        makeTM(dest);
        return dest.tm_year+DATETIME_START;
    }

    int datetime::month()const
    {
        tm dest;
        makeTM(dest);
        return dest.tm_mon+1;
    }

    int datetime::day()const
    {
        tm dest;
        makeTM(dest);
        return dest.tm_mday;
    }

    int datetime::hour()const
    {
        tm dest;
        makeTM(dest);
        return dest.tm_hour;
    }

    int datetime::minute()const
    {
        tm dest;
        makeTM(dest);
        return dest.tm_min;
    }

    int datetime::second()const
    {
        tm dest;
        makeTM(dest);
        return dest.tm_sec;
    }

З.І: зараз усвідомив, що трохи криво запостив, виправляюся. У такому вигляді код варто розбити на файл-заголовок та сирець-файл (див. коментарі).
На автоматі пропустив це, бо у моєму випадку клас має бути реалізований як чисто заголовковий.

I belong to the Dead Generation.
Подякували: Replace, leofun012

5 Востаннє редагувалося Bartash (29.12.2014 13:47:59)

Re: Потокозахищені альтернативи функцій у time.h

За результатати досліджень та деякого досвіду роботи підведу наступне резюме-повідомлення. Майбутнім сішникам та хрестоносцям варто звернути увагу на ці моменти та деякі граблі поміж них.

[upd] Повна стаття тут.
________________________

Робота із датою та часом на С/С++ певною мірою залежить від того, які інструменти обрано. Розглянемо деякі з них:

1. Стандартні можливості: заголовок <time.h> (ctime), що визначає структуру tm ("датачас+плюшки") та  декілька функцій для роботи із нею.

Ознаки
Плюси: цілковито підходять для навчання у ВНЗ та певної частини робочих проектів.
Мінуси:

  • граблі лежать у випадку багатопоточних програм, оскільки функції з time.h використовують єдину статичну змінну, на яку подейкуди повертають покажчик... Тож якщо працювати із такими засобами при багатопоточному режимі, використовуйте які-небудь блокувальні можливості: м'ютекси, наприклад (див. пост вище).

  • Формат даних має характерні ознаки прив'язки до мови та апаратних можливостей. Сіль буде у тому, що рік задається як зсув відносно 1900 року, місяці нумеруються з 0, а кількість секунд у полі tm_sec може коливатися від 0 до.. 61. Тож пильнуйте ретельно, що і як ви розумієте.

2. Клас QDateTime (QDate, QTime) від авторів бібліотеки Qt.
Загалом річ досить зручна, за можливостями місцями перегукується з аналогами на C#.

Ознаки
Плюси: зручно, не дуже громіздко, з ООП.
Мінуси: як мінімум, доведеться тягти за собою немаленьку DLL-ку або приєднати статично, що роздує екзешник до мегабайтних розмірів.

3. Клас wxDateTime від бібліотеки wxWidgets.
Альтернатива QDateTime, якщо, наприклад, постала необхідність переїхати на інші рельси на шляху "GUI - Client".

Ознаки

Плюси:

  • на порядок "легший" за "молочного брата" з Qt. Зокрема, у плані розмірів бібліотек на лінкування.

  • дозволяє працювати з датами, що були до 01.01.1900, чого не дозволяла стандартна tm структура.

Мінуси: як виявилося на практиці, структура tm таки лишила відбиток. По суті, wxDateTime за логікою представлення даних - це та ж tm, тільки без ліміту 1900 року. Таким чином, варто пам'ятати про вищезгадані tm-граблі (місяців, наприклад).

4. Можливості WinAPI. Представлені структурами (серед яких головна - SYSTEMTIME, хоча назва і не зовсім коректна) та набором функцій, деякі з яких наведені нижче (з MSDN):

Функції WinAPI для роботи з датою та часом

CompareFileTime 
DosDateTimeToFileTime 
FileTimeToDosDateTime 
FileTimeToLocalFileTime 
FileTimeToSystemTime 
GetFileTime 
GetLocalTime 
GetSystemTime 
GetSystemTimeAdjustment 
GetSystemTimeAsFileTime 
GetTickCount 
GetTimeZoneInformation 
LocalFileTimeToFileTime 
SetFileTime 
SetLocalTime 
SetSystemTime 
SetSystemTimeAdjustment 
SetTimeZoneInformation 
SystemTimeToFileTime 
SystemTimeToTzSpecificLocalTime

Ознаки

Плюси: мають переваги над time.h з точки зору безпеки даних.
Мінуси: платформо-залежні.

5. Клас COleDatetime та інші представники сімейства MFC.
Фактично - об'єктна обгортка над стандартними функціями WinAPI.

Ознаки

Плюси: зручніші за використання безпосередньо функцій.
Мінуси:

  • платформо-залежні.

  • місцями здійснюють операції з даними також у форматі tm-структури, що дозволяє поєднати 2 ув 1: граблі time.h із підводним камінням функцій WinAPI.

6. Boost. Клас Date_Time.
Ще один представник "холодної війни бібліотек". Загалом досить потужний, як і сама Boost-бібліотека.

Ознаки

Плюси: кросплатформовий, зручний, безпечний.
Мінуси: використання виправдане хіба що у випадку використання Boost в усій програмі у великих обсягах, оскільки бібліотека реалізована у великих заголовкових файлах та не дуже маленьких (однак на порядок менших за Qt) DLL/SLL-ках, що вплине на розмір кінцевого виконфайлу.

От такі пироги. Імовірніше за все, тут наведено далеко не повний список існуючих засобів, тому приймаються будь-які зауваження та пропозиції.

З.І:
Серед джерел, на які варто звернути увагу для детальнішого розбору згаданих вище інструментів, зазначу гарний посібник на CodeProject, завдяки якому отримано цікавинки про інструменти суто під Windows.

I belong to the Dead Generation.

6 Востаннє редагувалося -=ЮрА=- (30.01.2016 16:35:16)

Re: Потокозахищені альтернативи функцій у time.h

Bartash, питання про tm. По суті йде мова про задачу розділення доступу до спільного ресурсу.
Чому не використати

volatile tm pTm;

саме структуру а не поінтер.
кожне звертання до pTm - memcpy у чому проблема?Максимум можете пропустити встановку часу бо якийсь з потоків переб'є значення пізніше ніж інший зчитає, проте це проблема саме дизайну та синхронізації але такий дизайн зумовить пропуски часу навіть якщо time_t не використовувати.
Найбільш простою у використанні під він вважаю SYSTEM_TIME