我正在尝试实现一个简单的线程入门类。下面是一个简单基类实现和两个应该作为启动器的派生变体。第一个偶尔抛出static void* Threaded::run (void* self)
的段错误。我想这可能是一个指针问题,但我无法找出原因?
this
中的Threaded::start
是否指向错误的地址,或者我的第一次推导是否存在任何其他问题?
这就是它的用法:
Thread thread (ptr_to_some_obj);
thread.start (&this_obj::callback);
thread.detach ();
简单基类
class Threaded
{
public:
Threaded () {/* empty */}
virtual ~Threaded () {/* empty */}
/** Returns true if the thread was successfully started, false if there was an error starting the thread */
bool start ()
{
return (pthread_create (&_thread, NULL, run, this) == 0);
}
/** Implement this method in your subclass with the code which allows to gently stop execution. */
virtual void stop () = 0;
/** Will not return until the internal thread has exited. */
void wait ()
{
(void) pthread_join (_thread, NULL);
}
bool detach ()
{
return (pthread_detach (_thread) == 0);
}
protected:
/** Implement this method in your subclass with the code you want your thread to run. */
virtual void run () = 0;
static void* run (void* self)
{
((Threaded*) self) -> run ();
return NULL;
}
pthread_t _thread;
};
派生类1 (在上面((Threaded*) self) -> run ();
处抛出段错误)
typedef void (*staticcall)(void*);
class Thread : public Threaded
{
public:
Thread (void* passthru)
:_call (NULL)
{
_passthru = passthru;
}
~Thread () { /* empty */ }
bool start (staticcall call)
{
_call = call;
assert (_call);
return start ();
}
void stop ()
{
// nothing
}
protected:
void run ()
{
(_call) (_passthru);
}
bool start ()
{
return Threaded::start ();
}
private:
Thread () { };
void* _passthru;
staticcall _call;
};
派生类2 (有效,但我宁愿派生类1 实施)
typedef void (*staticcall)(void*);
class Thread2 : public Threaded
{
public:
Thread2 (void* passthru)
{
_passthru = passthru;
}
~Thread2 () { /* empty */ }
bool start (staticcall call)
{
_call = call;
assert (_call);
return start ();
}
void stop ()
{
// nothing
}
protected:
void run () { }
static void* run2 (void*)
{
(_call) (_passthru);
return NULL;
}
bool start ()
{
return (pthread_create (&_thread, NULL, run2, NULL) == 0);
}
private:
Thread2 () { };
static void* _passthru;
static staticcall _call;
};
void* Thread2::_passthru;
staticcall Thread2::_call;
答案 0 :(得分:0)
编辑:让线程作为方法调用执行的解决方案
我在最近的讨论中考虑的解决方案是如果线程入口点是一个简单的函数。
但是,我认为这个想法更多的是利用实际的对象,因此线程体实际上是body()
方法的调用。
这更棘手,因为在整个线程持续时间内必须有派生类的实时实例,但在start
/ detach
之后原始实例很可能超出范围序列
一种可能的(虽然有些代价高昂)解决方案是让线程主体存根在堆栈上创建原始实例的本地副本。所以线程启动器将构造一个线程对象,线程本身将复制它。
使用此系统,您只需确保原始实例将在pthread_create
和调度程序激活线程之间的时间间隔内保持实时。
这需要一个信号量(由于血腥的C ++ 11在商店中没有一个信号量,因此将使用互斥锁/ cond.var.对手动完成。)
要在基类中隐藏这个混乱的代码,您需要将基本指针向下转换为适当的子类类型。
我试图模仿基类,尽管可能有更聪明的解决方案。我想不出任何一个。
为了测试解决方案,我使用一个计数器系统来检测在线程存根可以制作本地副本之前是否删除了原始的Thread实例。
SYNC编译标志激活信号量。预期的程序输出为0->0
。如果出现其他数字,则表示某些线程在混乱的实例上运行。
我在虚拟机上的Ubuntu上进行了测试,看起来效果还不错。
#include <cstdlib>
#include <cstdio>
#include <cassert>
#include <thread> // sleep_for
#include <chrono> // milliseconds
#include <pthread.h>
#define SYNC // undefine this to see what happens without synchronization
typedef void *(*tEntryPoint) (void *);
#include <mutex>
#include <condition_variable>
class semaphore {
private:
std::mutex m;
std::condition_variable v;
int c;
public:
semaphore (int count = 0):c(count){}
void V()
{
#ifdef SYNC
std::unique_lock<std::mutex> l(m);
c++;
v.notify_one();
#endif
}
void P()
{
#ifdef SYNC
std::unique_lock<std::mutex> l(m);
while (c == 0) v.wait(l);
c--;
#endif
}
};
template<typename Derived>
class Threaded
{
public:
/** Returns true if the thread was successfully started, false if there was an error starting the thread */
bool start(void)
{
destructor_guard = new semaphore();
bool res = (pthread_create(&_thread, NULL, (tEntryPoint)entry_point, this) == 0);
if (res) destructor_guard->P(); // wait fot thread to start execution
delete destructor_guard;
return res;
}
/** This optional method will be executed after the thread main body */
virtual void stop() {}
/** Will not return until the internal thread has exited. */
void wait()
{
(void)pthread_join(_thread, NULL);
}
/** Will let the underlying task run independently */
bool detach()
{
return (pthread_detach(_thread) == 0);
}
private:
static void * entry_point(Derived * self)
{
Derived local_self = *self;
local_self.destructor_guard->V(); // original can be deleted
local_self.body();
local_self.stop();
return NULL;
}
pthread_t _thread;
semaphore* destructor_guard;
};
#define NUM_THREADS 9
#define REPEAT 3000
static int signature[NUM_THREADS + 1] = { 0, };
class Thread : public Threaded<Thread>
{
unsigned id;
public:
Thread(unsigned id) : id(id) {}
~Thread() { id = 0; }
void body(void)
{
signature[id%(NUM_THREADS+1)]++;
}
void stop(void)
{
std::this_thread::sleep_for(std::chrono::milliseconds(10));
signature[id%(NUM_THREADS+1)]++;
}
};
void launch_a_thread(int id)
{
Thread thread (id);
if (thread.start())
{
// thread.wait();
thread.detach();
}
}
int main(void)
{
for (unsigned i = 0; i != REPEAT*NUM_THREADS; i++) launch_a_thread(1+i%NUM_THREADS);
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // leave enough time for free running threads to terminate
for (int i = 0 ; i <= NUM_THREADS ; i++) if (signature[i] != 2*REPEAT) printf ("%d -> %d\n", i, signature[i]);
return 0;
}
答案 1 :(得分:0)
正如molbdnilo所指出的那样:
pthread_create
只排队新线程。无法保证何时调用线程函数,并且thread
当时必须处于活动状态。
由于我不想保留生成的线程列表,因此我使用pthread_cond_wait
和pthread_cond_signal
解决了这个问题。生成器将等待由线程中运行的方法发出的信号。这样线程创建者就可以在调用to-be-threaded方法之前销毁线程对象。
class ThreadSpawner
{
public:
ThreadSpawner ()
{
pthread_mutex_init (&MtxThreadStarter, 0);
pthread_cond_init (&CondThreadStarter, 0);
}
~ThreadSpawner ()
{
pthread_cond_destroy (&CondThreadStarter);
pthread_mutex_destroy (&MtxThreadStarter);
}
void spawn ()
{
Thread thread (pass_object);
pthread_mutex_lock (&MtxThreadStarter);
if (thread.start (&ThreadSpawner::callback))
{
// wait here for signal
pthread_cond_wait (&CondThreadStarter, &MtxThreadStarter);
thread.detach ();
}
pthread_mutex_unlock (&MtxThreadStarter);
}
static void callback (void* passthru)
{
// send signal to thread spawner
pthread_mutex_lock (&MtxThreadStarter);
pthread_cond_signal (&CondThreadStarter);
pthread_mutex_unlock (&MtxThreadStarter);
// do threaded work
}
private:
static pthread_mutex_t MtxThreadStarter;
static pthread_cond_t CondThreadStarter;
}
pthread_mutex_t ThreadSpawner::MtxThreadStarter = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t ThreadSpawner::CondThreadStarter = PTHREAD_COND_INITIALIZER;