c++ - 防止派生类被破坏

标签 c++ multithreading inheritance

我有一个带有 protected 构造函数的基类 IRunnable,因此无法创建该基类的实例。该类有一个纯虚方法 run(),必须由派生类实现。我将 IRunnable * 的指针传递给某种类似 java 的 Executor,它控制许多线程,并将这些 Runnable 分配给它们。 我的问题是,当这样一个 IRunnable * 指针指向 IRunnable 派生类的对象(位于另一个线程的堆栈上)被分配给工作线程时,我无法确定派生对象不是当工作线程仍在使用它时被销毁,因为在其线程上拥有该对象的线程可能会离开创建该对象的范围。 例如:

int main(void)
{
    CExecutor myExecutor(...); 
    for (UInt32 i = 0; i < 2; i++)
    {
        derivedRunnable myRunnable1; //will be destroyed after each loop iteration
        myExecutor.submit(myRunnable1);
    }
}

class derivedRunnable : public IRunnable
{
public:
    derivedRunnable(const char * name = NULL) : IRunnable(name) {}
    ~derivedRunnable() {}
    void run(void)
    {
        for (UInt32 i = 0; i < 100; i++)
        {
            char name [256] = {"\0"};

            pthread_getname_np(pthread_self(), name, 255);
            printf("%s in step %d\n", name, i);
        }
        fflush(stdout);
    }
};

我在基类 IRunnable 中实现了引用计数,并在析构函数中执行了阻塞调用,只有当使用它的最后一个线程取消注册时,该调用才会返回。问题是,派生类的析构函数首先被调用,因此在调用通过阻塞调用析构的基类之前,该对象将被部分析构。 在上面的示例中,我收到以下运行时错误:
调用纯虚方法
在没有事件异常的情况下终止调用 如果我在 .submit() 调用之后插入一些 usec 的 usleep,它将起作用,因为线程将在它被销毁之前完成可运行的任务

class IRunnable
{
    friend class CThread;
    friend class CExecutor;
private:
    CMutex mutx;
    CBinarySemaphore sem;
    UInt32 userCount;
    [...]
    virtual void run(void) = 0;
    IRunnable(const IRunnable & rhs); //deny
    IRunnable & operator= (const IRunnable & rhs); //deny

    void registerUser(void)
    {
        mutx.take(true);
        if (0 == userCount++)
            sem.take(true);
        mutx.give();
    }

    void unregisterUser(void)
    {
        mutx.take(true);
        if (0 == --userCount)
            sem.give();
        mutx.give();
    }

protected:
    IRunnable(const char * n = NULL)
    :mutx(true,false)
    ,sem(true,false)
    ,userCount(0)
    {
        setName(n);
    }

    ~IRunnable()
    {
        sem.take(true);
    }
    [...]

我能做什么?

最佳答案

如果您在析构函数中阻塞,那么您的提交循环将不会是异步的,这就是多线程的要点。生命周期必须持续超过分配范围。为此,您应该动态分配 IRunnable 对象,并让您的 Executor 取得所有权。当作业完成时,执行者将负责删除它。

更好的是,为此接口(interface)使用共享指针。这样调用线程仍然可以引用该对象(例如检查完成情况)。最后一个线程完成将删除该对象。

编辑:添加示例代码:

for (UInt32 i = 0; i < 2; i++)
{
    shared_ptr< derivedRunnable > myRunnable1(new derivedRunnable);
    myExecutor.submit(myRunnable1);
}

或者保存任务:

list < shared_ptr < derivedRunnable > > jobs;
for (UInt32 i = 0; i < 2; i++)
{
    shared_ptr< derivedRunnable > myRunnable(new derivedRunnable);
    myExecutor.submit(myRunnable);
    jobs.push_back(myRunnable);
}
// do other stuff
// call a status function that you haven't written yet:
jobs.front()->WaitForCompletion();

顺便说一句,您还应该考虑切换到 std::thread 或 boost::thread,具体取决于编译器的版本。

关于c++ - 防止派生类被破坏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20713284/

相关文章:

c++ - 指向对象 X 的 weak_ptr 在对象 X 销毁期间是否仍然有效

java - 如何使用来自 URL 的数据填充 javaFX 中的 ListView?

php - 数组的静态继承

C++ 默认构造函数语法

c++ - std::string 是全局变量时不能在 gdb 中打印,为什么?

JavaSE : First thread to call a method interrupts all other threads

javascript - 为什么Object.prototype的__proto__是另一个Object.prototype?

c# - 如何定义实现接口(interface)并约束类型参数的泛型类?

c++ - 基类和派生类的复制构造函数

linux - shell 脚本: Parallelizing commands in bash script under Ubuntu linux