我有一个类库程序集,一旦加载就打开一个表单(form1),当提示时,form1打开其他表单(form2)。每个表单都在一个单独的线程中运行,这是因为在每个表单中运行flashweave应用程序,并且为了性能,我需要在单独的theads中运行它们。 如果我使用用c#编写的托管加载程序加载库,一切正常。 如果我使用混合的clr / c ++程序集加载库,当form2关闭时,Application.Run()不会返回导致许多卡住的线程。 我也尝试使用Thread.Abort()强制中止线程,但线程仍然没有中止。 如果我关闭form1 application.run()返回,它的线程可以停止。 我还尝试打开简单的空表单而不是form2而没有任何flashwave对象,但它仍然没有返回。
也许问题与我得到的这条消息有关:
CLR无法过渡 从COM上下文0x197060到COM 上下文0x196ef0持续60秒。该 拥有目的地的线程 上下文/公寓是最有可能的 或者做一次非抽水等待或者 处理很长时间 没有泵Windows的操作 消息。这种情况一般都有 负面的业绩影响和可能 甚至导致应用程序成为 无响应或内存使用 随着时间的推移不断积累。至 一切都避免这个问题 线程公寓(STA)线程 应该使用抽等待原语 (例如CoWaitForMultipleHandles)和 经常在长时间内发送消息 正在运行。
关于form2开放:
private void OpenTable()
{
if (!this.InvokeRequired)
{
Thread TableRun = new Thread(new ThreadStart(OpenTable));
TableRun.ApartmentState = ApartmentState.STA;
TableRun.IsBackground = false;
TableRun.Name = "T2";
TableRun.Start();
return;
}
try
{
FormTable T = new FormTable(;
T.MyThread = Thread.CurrentThread;
Application.Run(T);
}
catch (Exception ex)
{
}
}
卡住线程的堆栈跟踪:
ntdll.dll!_ZwWaitForMultipleObjects@20() + 0x15字节ntdll.dll!_ZwWaitForMultipleObjects@20() + 0x15字节KernelBase.dll!_WaitForMultipleObjectsEx@20() + 0x36字节kernel32.dll!_WaitForMultipleObjectsExImplementation@20() + 0x8e byte user32.dll!_RealMsgWaitForMultipleObjectsEx@20() + 0xe2 byte ole32.dll!CCliModalLoop :: BlockFn()+ 0x96字节
ole32.dll!_CoWaitForMultipleHandles@20() - 0x51b9 byte mscorwks.dll!NT5WaitRoutine()+ 0x39 byte mscorwks.dll!MsgWaitHelper() + 0x97 byte mscorwks.dll!Thread :: DoAppropriateAptStateWait() - 0xf32e5 byte mscorwks.dll!Thread :: DoAppropriateWaitWorker() + 0x104 byte mscorwks.dll!Thread :: DoAppropriateWait() + 0x40 byte mscorwks.dll!CLREvent :: WaitEx()+ 0x1438a9字节
mscorwks.dll!CLREvent :: Wait()+ 0x17 字节
的Mscorwks.dll!WKS :: GCHeap :: FinalizerThreadWait() + 0xec byte mscorwks.dll!ReleaseRCWsInCaches()+ 0xe34fd字节
的Mscorwks.dll!ReleaseRCWsInCachesNoThrow() + 0x67 byte mscorwks.dll!Thread :: CleanupCOMState() + 0x1b8f83 byte mscorwks.dll!Thread :: OnThreadTerminate() + 0x46字节mscorwks.dll!DestroyThread()+ 0x3b 字节
的Mscorwks.dll!ThreadNative :: KickOffThread() + 0xf2 byte mscorwks.dll!Thread :: intermediateThreadProc() + 0x46 byte kernel32.dll!@ BaseThreadInitThunk @ 12() + 0x12字节
关于显示第一个帖子的代码是:
//c++ code
Assembly::form^ f= gcnew Assembly::form() ;
f->Load();
gcroot<Assembly::form^>* dsa3_gc_p= new gcroot<Assembly::form^>(f);
this->obMainLib = (void *)dsa3_gc_p;
//------------------------
//The c++ loader just calls the Load() method
//c#library
public void Load()
{
FormThread = new Thread(new ThreadStart(this.Start));
FormThread.Name = "T7";
FormThread.IsBackground = true;
FormThread.SetApartmentState(ApartmentState.STA);
FormThread.Start();
}
private void Start()
{
Config = GlobalConfig.GetConfig(GlobalConfig.ConfigurationFile);
HttpInterface = new DHttpInterface(Config);
Lobby = new FormLobby(HttpInterface, false);
WorkerThread = new Thread(new ThreadStart(this.Start));
WorkerThread.Name = "T6";
WorkerThread.IsBackground = true;
WorkerThread.ApartmentState = ApartmentState.STA;
WorkerThread.Start();
Application.Run(Lobby);
Config.SaveToDisk();
}
NEWS: 最后我发现了什么产生了这种行为。在实例化c#库之前,加载器尝试使用.net System :: Management来获取cpu序列,如果我删除这样的部分,那么一切正常。 这是有罪的部分:
std::string Loader::GetCPUID()
{
std::string lsCPUID = "";
try
{
System::Management::ManagementObjectCollection^ moReturn = nullptr;
System::Management::ManagementObjectSearcher^ moSearch ;
moSearch = gcnew System::Management::ManagementObjectSearcher("Select * from Win32_Processor");
moReturn = moSearch->Get();
for each ( System::Management::ManagementObject^ mo in moReturn )
{
char * chp = (char *) System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(mo["ProcessorID"]->ToString()).ToPointer();
lsCPUID.assign(chp);
}
}
catch(System::Exception^ ex )
{
}
return lsCPUID;
}
感谢。
答案 0 :(得分:0)
是否有可能从MTA线程调用OpenTable,InvokeRequired返回true,因此它不是创建另一个STA线程,而是直接在MTA线程上调用Application.Run()?
尝试从STA线程调用Application.Run()而不是......
答案 1 :(得分:0)
我终于找到了方法。我创建了一个新的AppDomain,我运行该方法来获取cpu序列,然后卸载该域,以便导致问题的System :: Management库被卸载。
ref class MarshalByRefType:MarshalByRefObject { 上市: //通过代理调用此方法。
std::string GetCPUID()
{
//char * chp = (char *) System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(R671::R671::Value()).ToPointer();
std::string lsCPUID = "";
//return lsCPUID.assign(chp);
try
{
System::Management::ManagementObjectCollection^ moReturn = nullptr;
System::Management::ManagementObjectSearcher^ moSearch ;
moSearch = gcnew System::Management::ManagementObjectSearcher("Select * from Win32_Processor");
moReturn = moSearch->Get();
for each ( System::Management::ManagementObject^ mo in moReturn )
{
char * chp = (char *) System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(mo["ProcessorID"]->ToString()).ToPointer();
lsCPUID.assign(chp);
}
}
catch(System::Exception^ ex )
{
}
AppDomainSetup^ ads = AppDomain::CurrentDomain->SetupInformation;
String^ str = String::Format("AppName={0}, AppBase={1}, ConfigFile={2}",
ads->ApplicationName,
ads->ApplicationBase,
ads->ConfigurationFile);
char * chp = (char *) System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(str).ToPointer();
lsCPUID.assign(chp);
return lsCPUID;
}
}
class Loader
{
std::string GetCPUID()
{
String^ callingDomainName = Thread::GetDomain()->FriendlyName;
// Construct and initialize settings for a second AppDomain.
AppDomainSetup^ ads = gcnew AppDomainSetup();
ads->ApplicationBase =
"file:///" + Assembly::GetExecutingAssembly()->Location;
ads->DisallowBindingRedirects = false;
ads->DisallowCodeDownload = true;
ads->ConfigurationFile =
AppDomain::CurrentDomain->SetupInformation->ConfigurationFile;
// Create the second AppDomain.
AppDomain^ ad2 = AppDomain::CreateDomain("AD #2",
AppDomain::CurrentDomain->Evidence, ads);
String^ sam = R671::R671::typeid->FullName;
// Create an instance of MarshalbyRefType in the second AppDomain.
// A proxy to the object is returned.
MarshalByRefType^ mbrt =
(MarshalByRefType^) ad2->CreateInstanceFromAndUnwrap(
Assembly::GetExecutingAssembly()->Location,
MarshalByRefType::typeid->FullName
);
string lsCPUID;
//char * chp = (char *) System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(mbrt2->Value()).ToPointer();
lsCPUID = mbrt->GetCPUID();
try
{
AppDomain::Unload(ad2);
}
catch ( AppDomainUnloadedException^ /*e*/ )
{
}
return lsCPUID;
}
void Load()
{
//do things
std:string cpuid = this->GetCPUID();
//do things
//load c# library and open forms
}
}