【问题标题】:C++/CLI Threading - InvalidProgramExceptionC++/CLI 线程 - InvalidProgramException
【发布时间】:2014-09-09 13:25:54
【问题描述】:

我正在使用 C++/CLI 编写一种下载器。它应该在进度条中显示下载状态。为了使下载不阻塞表单,我需要在后台线程中运行它,但是为了访问进度条,我需要在我的下载函数中指向它的指针。因此,我需要以表单(或进度条,无所谓)作为参数来启动线程。为此,我写了一个小线程包装类:

typedef System::Collections::Generic::List<System::Object^> ArgList;

ref class GCThread {

private:
    void (*entryPoint)(System::Object^);
    ArgList^ arguments;
public:
    GCThread(void (*entryPoint)(System::Object^)) { this->entryPoint = entryPoint; arguments = gcnew ArgList(); };
    void addArgument(System::Object^ obj) { arguments->Add(obj); };
    void start();
};

void GCThread::start() {

    System::Threading::ParameterizedThreadStart^ ts = gcnew System::Threading::ParameterizedThreadStart(&entryPoint);
    System::Threading::Thread^ t = gcnew System::Threading::Thread(ts);
    t->Start((System::Object^) arguments);
}

当尝试像这样调用函数时

GCThread^ thread = gcnew GCThread(downloadFiles);
thread->addArgument(btnLogin);
thread->addArgument(pgrDownload);
thread->start();

downloadFiles 是一个全局方法,定义为

void __clrcall downloadFiles(System::Object^);

我得到一个 System::InvalidProgramException: The Common Language Runtime has found an invalid program 在创建参数化线程启动对象时。我究竟做错了什么?为什么不能为 gc 类型创建全局变量?我知道拥有全局变量完全违背垃圾收集器的目的,但至少有机会自己决定会很好。

顺便说一句,我使用的是 VC++ 2008。 感谢您的宝贵时间!

【问题讨论】:

    标签: multithreading winforms visual-c++ c++-cli


    【解决方案1】:
      ... ts = gcnew System::Threading::ParameterizedThreadStart(&entryPoint)
    

    你欺骗了编译器接受一个无效的程序。该方法需要一个委托对象,你传递了一个非托管函数指针。这是编译器中的一个缺陷,它应该生成一个诊断。值得注意的是,IntelliSense 解析器确实会抱怨。当您删除 & 运算符时,它会变得更加壮观,这会导致编译器崩溃。可能应该为此使用正确的名称:编译器错误。

    您需要通过使用 delegate 关键字声明适当的委托类型来取得成功。重写你的代码(我认为)做你想做的事:

    using namespace System;
    
    typedef System::Collections::Generic::List<System::Object^> ArgList;
    
    ref class GCThread {
    public:
        delegate void EntryPoint(ArgList^ args);
    private:
        EntryPoint^ entryPoint;
        ArgList^ arguments;
        void startFunc(Object^ args);
    public:
        GCThread(EntryPoint^ start) { this->entryPoint = start; arguments = gcnew ArgList(); };
        void addArgument(System::Object^ obj) { arguments->Add(obj); };
        void start();
    };
    
    void GCThread::startFunc(Object^ args) {
        this->entryPoint(safe_cast<ArgList^>(args));    
    }
    
    void GCThread::start() {
        auto t = gcnew System::Threading::Thread(gcnew System::Threading::ParameterizedThreadStart(this, &GCThread::startFunc));
        t->Start(arguments);
    }
    

    示例用法:

    void ExampleThread(ArgList^ args) {
        // etc..
    }
    ....
    auto t = gcnew GCThread(gcnew GCThread::EntryPoint(&ExampleThread));
    t->addArgument(42);
    t->start();
    

    解决这个问题的痛苦少得多的方法是只声明一个简单的类类型,它只存储参数并使用普通线程。在线程函数中使用 safe_cast 来恢复参数。在您的情况下甚至不需要,因为您想使用 List。换句话说:

    void ExampleThread(Object^ arg) {
        auto args = safe_cast<List<Object^>^>(arg);
        // etc...
    }
    ...
    
    auto arg = gcnew List<Object^>;
    arg->Add(42);
    auto t2 = gcnew Thread(gcnew ParameterizedThreadStart(&ExampleThread2));
    t2->Start(arg);
    

    【讨论】:

    • 完美,这正是它应该做的。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-19
    相关资源
    最近更新 更多