Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Singleton в нескольких потоках


Автор: azesmcar 24.3.2009, 13:42
Добрый день,

Есть класс SomeThread который делает конкретную задачу..обьектов этого класса - несколько. Каждый обьект имеет свое сойденение с базой данных и в принципе абсолютно изолирован (т.е. никаких глобальных данных и тому подобного, каждый обьект оперирует со своими локальными данными). Но как понимаете весь функционал не написан в модуле SomeThread, а он использует некие вспомогательные классы SystemUser, UserActionHandler и тому подобное. Вопрос в том что в каждом модуле мне нужно подключение к базе данных которое создано в классе SomeThread, а передавать его в каждый модуль, как-то некрасиво. Можно конечно создать синглтон, но..это потоки, т.е. на каждый поток должно быть свое подключение. Как это можно сделать? Было бы красиво что-то вроде

Код

class SystemUser
{
public:
   bool Logon(const std::string login, const std::string password) {
      ThreadContext<тут что-то передать>::DBConnection()->Query( ... );
   }
}


но вот как это реализовать? 

Автор: Lazin 24.3.2009, 14:07
например, с помощью boost::call_once

Автор: azesmcar 24.3.2009, 14:16
Цитата

например, с помощью boost::call_once 


а без буста? меня больше принцип реализации интересует

Автор: jonie 24.3.2009, 22:36
если я правильно понял задачу, то на каждый поток у вас свое подключение.
почему нельзя сделать в том классе что вы привели static map<threadId, connectionPtr> connections и при каждом обращении из конкретного потока (его id мы всегда сможем узнать) в каждой функции либо брать кешированное соединение либо создавать, пихать в кеш и использовать ?

замечания:
1) map это НЕ std::map ! т.к. последний не потокобезопасен (хотя можно доступ к нему сделать таковым)
2) кешировать соединение не очень хорошо (точнее очень нехорошо). могут быть проблемы. все зависит от задачи.
3) конечно нужна функции "отключится от объекта"

Автор: Cтpaнник 24.3.2009, 23:13
Если дело происходит в Windows и кроссплатформенность кода не нужна, то имеет смысл посмотреть в сторону платформенно-специфичных средств (а именно, в Win это TLS). 
Например, в MSVC++ есть конструкция __declspec(thread) <тип данных> <имя переменной>; - таким образом создается экземпляр переменной, специфичный для вызывающего потока. Это позволит разделить connectionPtr и за каждым потоком закрепить свой экземпляр оного. Если применить еще и идиому RTTI, то, возможно, удастся решить и проблемы с "зависшими" или незакрытыми соединениями.... 

Автор: Lazin 24.3.2009, 23:16
я совсем не понял задачи, но предположу, что лучше совсем обойтись без синглтона, вместо этого, можно передавать объект-подключение, через параметр конструктора потока, это сделает реализацию более гибкой, к тому-же, упростит написание тестов...

Добавлено через 6 минут и 1 секунду
Цитата(Cтpaнник @  24.3.2009,  23:13 Найти цитируемый пост)
Например, в MSVC++ есть конструкция __declspec(thread) <тип данных> <имя переменной>; - таким образом создается экземпляр переменной, специфичный для вызывающего потока. Это позволит разделить connectionPtr и за каждым потоком закрепить свой экземпляр оного. Если применить еще и идиому RTTI, то, возможно, удастся решить и проблемы с "зависшими" или незакрытыми соединениями....  

только если ты не пишешь разделяемую библиотеку smile 

Автор: azesmcar 25.3.2009, 06:04
Цитата

если я правильно понял задачу, то на каждый поток у вас свое подключение.


Абсолютно верно.
Цитата

почему нельзя сделать в том классе что вы привели static map<threadId, connectionPtr> connections и при каждом обращении из конкретного потока (его id мы всегда сможем узнать) в каждой функции либо брать кешированное соединение либо создавать, пихать в кеш и использовать ?


так и думал сделать, но
Цитата

1) map это НЕ std::map ! т.к. последний не потокобезопасен (хотя можно доступ к нему сделать таковым)

если учесть что в этот мап значения добавляются всего один раз в начале одним потоком, а потом ТОЛЬКО читают из него методом find, могут ли быть проблемы..со вчерашнего дня об этом и думаю.
Цитата

Если дело происходит в Windows и кроссплатформенность кода не нужна, то имеет смысл посмотреть в сторону платформенно-специфичных средств (а именно, в Win это TLS). 


нет, мне скорее нужно под линукс..но код кроссплатформенный.

Автор: jonie 25.3.2009, 07:54
Цитата

если учесть что в этот мап значения добавляются всего один раз в начале одним потоком, а потом ТОЛЬКО читают из него методом find, могут ли быть проблемы..со вчерашнего дня об этом и думаю.
сама карта, конечно, ничего делать не будет "просто так", но т.к. класс не потокобезопасен, потоковые операции все же стоит производить атомарно - "малоли чего".

Автор: azesmcar 27.3.2009, 08:43
Цитата

сама карта, конечно, ничего делать не будет "просто так", но т.к. класс не потокобезопасен, потоковые операции все же стоит производить атомарно - "малоли чего".


ну это понятно, но тут есть что-то что мне не нравится, фактически потоки, у которых свое сойденение к базе должны ждать, хоть и недолго, но все же ждать пока поиск закончится..не нравится мне это. Может стоит сменить архитектуру пока не поздно? Но другого ничего не приходит на ум. Не писать же все в одном модуле? Никто не сталкивался с подобным что ли? Синглтон очень нужная в нашей жизни штука, а многопоточное приложение в наше время не редкость..

Автор: azesmcar 27.3.2009, 09:50
Все!!! управился, привожу решение кому интересно.
В виндоуз есть такая штука как TLS (Thread Local Storage). Вещь полезная и полностью решающая данную задачу. В никсах она добавлена как патч, но это было давно и в принципе на данный момент вы вряд ли найдете систему которая не поддерживает TLS.
Код

#ifdef WIN32
#define TLS __declspec( thread ) static
#else
#include <pthread.h>
#define TLS __thread static
#endif


вот и все..обявляем статические переменные как
Код

void ThreadProc(void* lpParameter)
{
    TLS int p = 0;
}


и наслаждаемся Thread Scope Static переменными!

Всем спасибо.

Автор: Lazin 27.3.2009, 11:12
Цитата(azesmcar @  27.3.2009,  09:50 Найти цитируемый пост)
__declspec( thread )

нельзя использовать в dll, если ты будет загружать dll динамически, то будут проблемы

Добавлено через 7 минут и 7 секунд
workaround
Код

template <typename T>
class ThreadLocal
{
private:
    DWORD threadLocalIndex;

    ThreadLocal(ThreadLocal const&);

    T *GetPointer(void)
    {
        return static_cast<T*>(::TlsGetValue(threadLocalIndex));
    }

    void SetPointer(T *value)
    {
        ::TlsSetValue(threadLocalIndex, static_cast<void*>(value));
    }
public:
    void SetValue(const T &value)
    {
        T* currentPointer = GetPointer();
        if (currentPointer == NULL)
        {
            SetPointer(new T(value));
        }
        else
        {
            *currentPointer = value;
        }
    }

    T &GetValue()
    {
        T* currentPointer = GetPointer();
        if (currentPointer == NULL)
        {
            SetPointer(new T());
        }
        return *GetPointer();
    }

    operator T() 
    {
        return GetValue();
    }

    ThreadLocal<T>& operator = (const T& value)
    {
        SetValue(value);
        return *this;
    }

    void DeleteValue()
    {
        T* currentPointer = GetPointer();
        if (currentPointer != NULL)
        {
            delete currentPointer;
            SetPointer(NULL);
        }
    }

    ThreadLocal(const T& value)
    {
        threadLocalIndex = ::TlsAlloc();
        SetValue(value);
    }

    ThreadLocal()
    {
        threadLocalIndex = ::TlsAlloc();
    }

    ~ThreadLocal()
    {
        DeleteValue();
        ::TlsFree(threadLocalIndex);
    }
};

Автор: azesmcar 27.3.2009, 13:36
Цитата

нельзя использовать в dll, если ты будет загружать dll динамически, то будут проблемы


я и не собираюсь, но спасибо за информацию, почитаю про это.

Автор: 0xDX 28.3.2009, 03:42
Делай селектор,

А в селекторе возвращаемые объекты вычисляй по ID потока

Что то вроде
switch(IdThread)
{
case First:
       retunrn array_object[1];
case Second:
       retunrn array_object[1];
default:
return 0;
}

Весь код я не писал, если надо то скажи.

Автор: azesmcar 28.3.2009, 09:13
0xDX  smile 

1. Количество потоков - динамическое, еслиб я точно знал их количество - я бы придумал решения и покрасивей..хотя бы с шаблонами.
2. Откуда ты возьмешь First и Second? Это не константные значения. ИД потока изменяется при каждом запуске программы.
3. Чем тебе не нравится мое решение? Думаешь свитч красивее?

Автор: 0xDX 30.3.2009, 00:54
azesmcar  на винд работать не будет.

Цитата

1. Количество потоков - динамическое, еслиб я точно знал их количество - я бы придумал решения и покрасивей..хотя бы с шаблонами.
2. Откуда ты возьмешь First и Second? Это не константные значения. ИД потока изменяется при каждом запуске программы.


Ассоциативный контейнер полечит.

Автор: jonie 30.3.2009, 08:45
я собственно предлагал использовать map-у и настоящий синглетон (только сделать его потокобезопасным)... этот вариант хотябы кросплатформенный, чего не скажешь о каком-то TLS-е...

еще непонятно как этот TLS покажет себя в отладке...

Автор: azesmcar 30.3.2009, 08:52
Цитата

azesmcar  на винд работать не будет.


уже работает
Цитата

я собственно предлагал использовать map-у и настоящий синглетон (только сделать его потокобезопасным)... этот вариант хотябы кросплатформенный, чего не скажешь о каком-то TLS-е...


а чем этот ненастоящий? это статик изолированный в потоке, об этом заботится сама система а не мы..товарищи, давайте говорить обоснованно, я не утверждаю что TLS - это шедевр созданный великими умами, я о нем недавно прочитал..и прочитал в статье по решению именно моей проблемы..если у кого-то есть данные что ТЛС не есть хорошо поделитесь, я не спорю..просто писать до хрена кода только потому что непонятно как ТЛС покажет себя в отладке - по моему не стоит..можно проверить..вроде до сих пор проблем не было, технология не новая..в чем проблема то?

Автор: Lazin 30.3.2009, 09:08
в использовании локальной памяти потока нет ничего плохого, в pthread, AFAIK, это то-же возможно...

Автор: azesmcar 30.3.2009, 09:14
Цитата

в использовании локальной памяти потока нет ничего плохого


я тоже немало об этом прочитал, ничего плохого не увидел..

Цитата

в pthread, AFAIK, это то-же возможно... 


решение которе я написал - кроссплатформенное. там ifdef стоит. Проверял, работает на ура.


Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)