2009-06-26 2 views
2

У меня есть объект, для которого я хотел бы отслеживать количество потоков, ссылающихся на него. В общем, когда вызывается какой-либо метод объекта, я могу проверить локальное логическое значение потока, чтобы определить, был ли счет обновлен для текущего потока. Но это не помогает мне, если пользователь говорит, использует boost :: bind для привязки моего объекта к функции boost :: и использует это для запуска boost :: thread. Новый поток будет иметь ссылку на мой объект и может держаться за него в течение неопределенного периода времени, прежде чем вызывать какие-либо его методы, что приведет к устаревшему счету. Я мог бы написать свою собственную оболочку вокруг boost :: thread, чтобы справиться с этим, но это не помогает, если пользователь boost :: bind - это объект , который содержит мой объект (я не могу специализироваться на основе наличия типа элемента - по крайней мере, я не знаю, как это сделать) и использует это для запуска boost :: thread.Обнаруживает, когда объект передается в новый поток на C++?

Есть ли способ сделать это? Единственное, что я могу придумать, требует слишком много работы от пользователей - я предоставляю оболочку вокруг boost :: thread, которая вызывает специальный метод hook для передаваемого объекта при условии, что он существует, и пользователи добавляют специальный метод hook в любой класс который содержит мой объект.

Редактировать: Ради этого вопроса мы можем предположить, что я управляю средствами для создания новых потоков. Поэтому я могу включить boost :: thread, например, и ожидать, что пользователи будут использовать мою завернутую версию, и не придется беспокоиться о пользователях одновременно с использованием pthreads и т. Д.

Edit2: Можно также предположить, что у меня есть некоторые средства потока местное хранилище, через __thread или boost::thread_specific_ptr. Это не в нынешнем стандарте, но, надеюсь, скоро.

+1

Я думаю, что очень сложно решить вашу проблему. Это потому, что ссылки - это бит или память, указывающие на другую ячейку памяти (+ более или менее дополнительный материал). Поскольку память разделяется между потоками, вы не можете определить, какой поток принадлежит ссылке, просто потому, что все потоки «владеют» памятью, все они владеют ссылками. Это не значит, что это невозможно, но поскольку техническая база всегда делится вещами, это трудно сделать. – mmmmmmmm

+0

Да, я надеялся, что, контролируя, как создаются потоки, я мог бы как-то обойти это. Я редактировал свой пост, чтобы сделать эту возможность понятной. –

ответ

3

В целом, это сложно. Вопрос о том, «кто имеет ссылку на меня?» обычно не разрешается в C++. Возможно, стоит посмотреть на большую картину конкретной проблемы (проблем), которую вы пытаетесь решить, и посмотреть, есть ли лучший способ.

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

Вы можете установить концепцию «владеющего потока» для объекта и выполнить операции REJECT из любого другого потока, элементов GUI la Qt GUI. (Обратите внимание, что попытка сделать что-то безопасно с потоками, отличными от владельца, на самом деле не даст вам потоковой безопасности, поскольку, если владелец не проверен, он может столкнуться с другими потоками.) Это, по крайней мере, быстрое поведение.

Вы можете поощрять подсчет ссылок, поскольку объекты, видимые пользователем, являются легкими ссылками на сам объект реализации [и путем документирования этого!]. Но определенные пользователи могут обойти это.

И вы можете объединить эти два - то есть вы можете иметь представление о принадлежности нитей для каждой ссылки , а затем узнать, кому принадлежит эта ссылка. Это может быть очень мощным, но не совсем идиотским.

Вы можете начать ограничивать то, что пользователи могут и не могут сделать с объектом, но я не думаю, что покрытие больше, чем очевидные источники непреднамеренной ошибки. Должен ли быть объявлен оператор & личным, чтобы люди не могли принимать указатели на ваши объекты? Должны ли вы препятствовать динамическому распределению вашего объекта? В какой-то степени это зависит от ваших пользователей, но имейте в виду, что вы не можете помешать ссылки на объекты, поэтому в конечном итоге игра в битку-моль приведет вас в безумное состояние.

Итак, вернемся к моему первоначальному предложению: переанализируйте общую картину, если это возможно.

1

Коротких реализации Pimpl стиля, что делает ThreadId проверки перед каждым разыменованием я не вижу, как вы могли бы сделать это:

class MyClass; 
class MyClassImpl { 
    friend class MyClass; 
    threadid_t owning_thread; 
public: 
    void doSomethingThreadSafe(); 
    void doSomethingNoSafetyCheck(); 
}; 

class MyClass { 
    MyClassImpl* impl; 
public: 
    void doSomethine() { 
     if (__threadid() != impl->owning_thread) { 
      impl->doSomethingThreadSafe(); 
     } else { 
      impl->doSomethingNoSafetyCheck(); 
     } 
    } 
}; 

Примечания: Я знаю, что ОП хочет перечислить темы с активными указателями, Я не думаю, что это возможно. Вышеупомянутая реализация, по крайней мере, позволяет объекту знать, когда могут возникнуть разногласия. Когда изменить owning_thread сильно зависит от того, что делает doSomething.

1

Обычно вы не можете делать это программно.

К сожалению, путь для разработки заключается в том, чтобы спроектировать вашу программу таким образом, чтобы вы могли доказать (то есть убедить себя), что некоторые объекты являются общими, а другие - частными.

В текущем стандарте C++ нет даже понятия потока, поэтому нет стандартного портативного понятия локального хранилища потоков, в частности.

+0

Я готов использовать __thread и boost :: thread_specific_ptr для своих целей. Я отредактирую свое оригинальное сообщение, чтобы это отразить. Я думаю, вы все еще правы в целом. –

+0

Если вы узнаете разумный способ, дайте мне знать. Мне жаль, что я не могу дать вам более конкретную помощь здесь. – Tobias

0

Если я правильно понял вашу проблему, я считаю, что это можно сделать в Windows с использованием функции Win32 GetCurrentThreadId(). Ниже приведен быстрый и грязный пример того, как его можно использовать. Синхронизация потоков должна выполняться с помощью объекта блокировки.

Если вы создаете объект CMyThreadTracker в верхней части каждой функции-члена вашего объекта для отслеживания потоков, то _handle_vector должен содержать идентификаторы потоков, которые используют ваш объект.

#include <process.h> 
#include <windows.h> 
#include <vector> 
#include <algorithm> 
#include <functional> 

using namespace std; 


class CMyThreadTracker 
{ 

    vector<DWORD> & _handle_vector; 
    DWORD _h; 
    CRITICAL_SECTION &_CriticalSection; 
public: 
    CMyThreadTracker(vector<DWORD> & handle_vector,CRITICAL_SECTION &crit):_handle_vector(handle_vector),_CriticalSection(crit) 
    { 
     EnterCriticalSection(&_CriticalSection); 
     _h = GetCurrentThreadId(); 
     _handle_vector.push_back(_h); 
     printf("thread id %08x\n",_h); 
     LeaveCriticalSection(&_CriticalSection); 
    } 

    ~CMyThreadTracker() 
    { 
     EnterCriticalSection(&_CriticalSection); 
     vector<DWORD>::iterator ee = remove_if(_handle_vector.begin(),_handle_vector.end(),bind2nd(equal_to<DWORD>(), _h)); 
     _handle_vector.erase(ee,_handle_vector.end()); 
     LeaveCriticalSection(&_CriticalSection); 
    } 
}; 

class CMyObject 
{ 
    vector<DWORD> _handle_vector; 

public: 
    void method1(CRITICAL_SECTION & CriticalSection) 
    { 
     CMyThreadTracker tt(_handle_vector,CriticalSection); 

     printf("method 1\n"); 

     EnterCriticalSection(&CriticalSection); 
     for(int i=0;i<_handle_vector.size();++i) 
     { 
      printf(" this object is currently used by thread %08x\n",_handle_vector[i]); 
     } 
     LeaveCriticalSection(&CriticalSection); 

    } 
}; 

CMyObject mo; 
CRITICAL_SECTION CriticalSection; 

unsigned __stdcall ThreadFunc(void* arg) 
{ 

    unsigned int sleep_time = *(unsigned int*)arg; 
    while (true) 
    { 
     Sleep(sleep_time); 
     mo.method1(CriticalSection); 
    } 

    _endthreadex(0); 
    return 0; 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 

    HANDLE hThread; 
    unsigned int threadID; 

    if (!InitializeCriticalSectionAndSpinCount(&CriticalSection, 0x80000400)) 
     return -1; 

    for(int i=0;i<5;++i) 
    { 
     unsigned int sleep_time = 1000 *(i+1); 

     hThread = (HANDLE)_beginthreadex(NULL, 0, &ThreadFunc, &sleep_time, 0, &threadID); 
     printf("creating thread %08x\n",threadID); 
    } 

    WaitForSingleObject(hThread, INFINITE); 

    return 0; 
} 

EDIT1: Как уже упоминалось в комментарии, ссылки дозирующее может быть реализован, как показано ниже. Вектор может содержать уникальные идентификаторы потоков, относящиеся к вашему объекту. Вам также может потребоваться реализовать пользовательский оператор присваивания для обработки ссылок на объекты, которые копируются другим потоком.

class MyClass 
{ 
public: 
static MyClass & Create() 
    { 
    static MyClass * p = new MyClass(); 

    return *p; 
    } 
    static void Destroy(MyClass * p) 
    { 
     delete p; 
    } 

private: 
    MyClass(){} 
    ~MyClass(){}; 
}; 

class MyCreatorClass 
{ 
    MyClass & _my_obj; 
public: 
MyCreatorClass():_my_obj(MyClass::Create()) 
    { 

    } 

    MyClass & GetObject() 
    { 
     //TODO: 
     // use GetCurrentThreadId to get thread id 
     // check if the id is already in the vector 
     // add this to a vector 

     return _my_obj; 
    } 

    ~MyCreatorClass() 
    { 
     MyClass::Destroy(&_my_obj); 
    } 

}; 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    MyCreatorClass mcc; 

    MyClass &o1 = mcc.GetObject(); 

    MyClass &o2 = mcc.GetObject(); 

    return 0; 
} 
+0

Проблема с таким решением заключается в том, что новый поток может быть привязан к ссылке на объект без вызова каких-либо его методов до тех пор, пока не пройдет какое-то время. Если я хочу предпринять какие-либо действия, когда количество ссылок достигает определенного значения, это проблема, поскольку значение может быть неправильным на некоторое время. Я мог бы написать пользовательскую функцию start_new_thread, которая бы удостоверилась, что был вызван какой-то метод для объекта, но это не решает проблему объектов, содержащих мой объект. –

+0

Я предполагаю, что в этом случае вы можете применить заводские шаблоны, отпускающие ссылки на ваш объект. И запишите идентификаторы потоков в заводскую функцию. – Indy9000

+0

@JG wow, это понятно, но становится еще более сложным, когда вам нужно посчитать даже ссылки UNUSED. Идея фабрики Indeera может работать лучше. Хотя ссылка-счетная стратегия указателя может быть возможна, как и зародыш идеи. – SPWorley

0

Решение Я знаком с это утверждать «если вы не используете правильный API для взаимодействия с этим объектом, то все ставки выключены.»

Возможно, вы сможете обратиться к своим требованиям и сделать возможными любые потоки, ссылающиеся на объект subscribe to signals from the object. Это не поможет в условиях гонки, но позволяет потокам знать, когда объект сам выгрузился (например).

0

Для решения проблемы «У меня есть объект и вы хотите знать, сколько потоков обращается к нему», и вы также можете перечислять свои потоки, вы можете решить эту проблему с помощью локального хранилища потоков. Выделите индекс TLS для своего объекта. Сделайте частный метод под названием «registerThread», который просто устанавливает поток TLS для указания на ваш объект.

Ключ расширения к оригинальной идее плаката заключается в том, что во время каждый вызов вызовите этот registerThread(). Тогда вам не нужно определять, когда или кто создал поток, он просто установлен (часто избыточно) во время каждого фактического доступа.

Чтобы посмотреть, к каким потокам обратились к объекту, просто просмотрите их значения TLS.

Поверхность: простая и довольно эффективная.

Даунсайд: решает вопрос, но не распространяется гладко на несколько объектов или динамических потоков, которые не перечислены.

+0

Это, к сожалению, не работает, см. Мой комментарий к запросу Indeera. –

 Смежные вопросы

  • Нет связанных вопросов^_^