C++ 在C+中检测对象何时传递到新线程+;?
我有一个对象,我想跟踪引用它的线程数。通常,当调用对象上的任何方法时,我可以检查线程本地布尔值,以确定当前线程的计数是否已更新。但是,如果用户说,使用boost::bind将我的对象绑定到boost::函数并使用它启动boost::线程,那么这对我没有帮助。新线程将有一个对我的对象的引用,并且在调用它的任何方法之前可能会无限期地保留它,从而导致过时计数。我可以围绕boost::thread编写自己的包装器来处理这个问题,但如果用户boost::bind是一个包含我的对象的对象(我不能根据成员类型的存在进行专门化——至少我不知道有什么方法可以做到这一点),并使用它来启动boost::thread,那就没有帮助了 有没有办法做到这一点?我能想到的唯一方法需要用户做太多的工作——我在boost::thread周围提供了一个包装器,该包装器在传入的对象上调用一个特殊的钩子方法,前提是它存在,并且用户将这个特殊的钩子方法添加到包含我的对象的任何类中 编辑:对于这个问题,我们可以假设我控制了生成新线程的方法。因此,我可以包装boost::thread,例如,希望用户使用我的包装版本,而不必担心用户同时使用pthreads等C++ 在C+中检测对象何时传递到新线程+;?,c++,multithreading,boost,hook,reference-counting,C++,Multithreading,Boost,Hook,Reference Counting,我有一个对象,我想跟踪引用它的线程数。通常,当调用对象上的任何方法时,我可以检查线程本地布尔值,以确定当前线程的计数是否已更新。但是,如果用户说,使用boost::bind将我的对象绑定到boost::函数并使用它启动boost::线程,那么这对我没有帮助。新线程将有一个对我的对象的引用,并且在调用它的任何方法之前可能会无限期地保留它,从而导致过时计数。我可以围绕boost::thread编写自己的包装器来处理这个问题,但如果用户boost::bind是一个包含我的对象的对象(我不能根据成员类型
Edit2:我们也可以假设我有一些线程本地存储的方法,通过
\uuuuthread
或boost::thread\uptr
。它不在当前的标准中,但希望很快就会出现。缺少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();
}
}
};
注意:我知道OP想要列出带有活动指针的线程,我认为这是不可行的。上述实现至少让对象知道何时可能存在争用。何时更改所属线程在很大程度上取决于doSomething的功能。一般来说,这很难。“谁有我的参考”这个问题在C++中通常是不可解决的。也许有必要看看你正在试图解决的具体问题的大局,看看是否有更好的方法 有几件事我可以想出,可以让你半途而废,但没有一件是你想要的 您可以为一个对象建立“拥有线程”的概念,并拒绝来自任何其他线程的操作,即一个Qt GUI元素。(请注意,尝试从所有者以外的线程安全地执行操作实际上不会给您线程安全性,因为如果不检查所有者,它可能会与其他线程冲突。)这至少会给您的用户带来快速失败行为 您可以通过让用户可见的对象成为对实现对象本身的轻量级引用来鼓励引用计数[并对此进行记录!]。但是有决心的用户可以解决这个问题 您可以将这两者结合起来——也就是说,您可以对每个引用拥有线程所有权的概念,然后让对象知道谁拥有这些引用。这可能是非常强大的,但不是真正的白痴证明 您可以开始限制用户可以和不能对对象执行的操作,但我认为只涵盖意外错误的明显来源是不值得的。您是否应该声明operator&private,这样人们就不能获取指向您的对象的指针?你应该阻止人们动态分配你的对象吗?这在某种程度上取决于你的用户,但请记住你不能阻止对对象的引用,所以最终玩“打鼹鼠”游戏会让你发疯
因此,回到我最初的建议:如果可能的话,重新分析全局。通常你不能通过编程来完成这项工作 不幸的是,我们要做的就是以这样一种方式设计您的程序:您可以证明(即说服自己)某些对象是共享的,而其他对象是线程私有的
当前的C++标准甚至没有线程的概念,因此没有标准的线程本地存储的可移植概念,尤其是
< P>如果我正确地理解了你的问题,我相信这可以在Win32中使用Win32函数来完成。 下面是一个如何使用它的快速而肮脏的例子。线程同步应该使用锁对象来完成 如果在要跟踪线程的对象的每个成员函数的顶部创建CMyThreadTracker对象,则\u handle\u vector
应包含使用对象的线程ID
#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;
}
我所熟悉的解决方案是声明“如果您没有使用正确的API与此对象交互,那么所有的赌注都没有了。” 您可以改变您的需求,使引用该对象的任何线程都成为可能。这对竞争条件没有帮助,但允许线程知道对象何时卸载了自身(例如)。要解决“我有一个对象,想知道有多少线程访问它”的问题,并且您还可以枚举线程,您可以使用线程本地存储来解决此问题。 为对象分配TLS索引。创建一个名为“registerThread”的私有方法,该方法只需将线程TLS设置为指向您的对象
#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;
}
海报原始想法的关键扩展是在每次方法调用期间,调用这个registerThread()。然后,您不需要检测线程是何时或谁创建的,它只是在每次实际访问期间设置的(通常是冗余的)
要查看哪些线程访问了对象,只需检查它们的TLS值
优点:简单而高效
缺点:解决了发布的问题,但不能顺利扩展到不可枚举的多个对象或动态线程。我认为解决您的问题非常困难