C ++ / CLI线程 - InvalidProgramException

时间:2014-07-18 11:57:03

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

我正在使用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:公共语言运行时在创建参数化线程起始对象时发现了一个无效的程序。我究竟做错了什么?为什么不能为gc类型创建全局变量?我知道完全违背垃圾收集器的目的是拥有全局变量,但至少有机会自己决定会很好。

顺便说一下,我正在使用VC ++ 2008。 谢谢你的时间!

1 个答案:

答案 0 :(得分:4)

  ... ts = gcnew System::Threading::ParameterizedThreadStart(&entryPoint)

你欺骗了编译器接受一个无效的程序。该方法需要委托对象,您传递了一个非托管函数指针。这是编译器中的一个缺陷,它应该已经生成了诊断。值得注意的是,IntelliSense解析器确实抱怨。当你删除&amp; operator,调用编译器崩溃。可能应该使用正确的名称:编译器错误。

您需要通过使用 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();

更简单的方法是声明一个只存储参数并使用vanilla Thread的简单类类型。使用safe_cast&lt;&gt;在线程函数中恢复参数。在您的情况下甚至不需要使用List&lt;&gt;。换句话说:

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);