我有一个带有 protected 构造函数的基类IRunnable,因此无法创建该基类的实例。该类具有纯虚拟方法run(),必须由派生类实现。我正在将IRunnable *的指针传递给某种类似于Java的Executor,该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中实现了引用计数,并且在析构函数中进行了阻塞调用,该调用仅在使用它的最后一个线程向其注销时才会返回。问题是,派生类get的析构函数首先被调用,因此在调用阻塞调用所破坏的基类之前,对象将被部分破坏。
在上面的示例中,我得到以下运行时错误:pure virtual method calledterminate called without an active exception如果我在.submit()调用之后插入一个uuse的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);
    }
    [...]

我能做什么?

最佳答案

如果阻塞析构函数,则您的Submit循环将不是异步的,这是多线程的重点。生存期必须超过分配范围。为此,您应该动态分配IRunnable对象,并让您的执行者获得所有权。作业完成后,执行者将负责将其删除。

更好的是,为此接口(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。

09-27 05:28