垃圾收集器的线程问题 [英] Threading problem with Garbage Collector

查看:74
本文介绍了垃圾收集器的线程问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们有一个没有窗口的组件。好吧,托管代码中没有窗口 - 它

使用一个本身使用窗口的DLL,这就是我们的问题!


当垃圾收集器运行时删除我们的组件(通过按钮点击动态创建

,然后不再引用),

GC在不同的线程中运行,禁止DLL销毁它的

窗口,当调用该窗口的WndProc时产生一个GPF -

代码消失(DLL卸载),但窗口仍然存在.. ..


清楚地看到(花了我们几个小时,说实话,了解导致

GPF的原因,因为如果WinDBG以某种方式使用100%CPU负载,用于.NET 2.0),这是一个很好的猜测。


有没有办法解决这个问题,以便我们可以在原始主题的上下文中从Dispose()调用例程



我们尝试创建一个窗口(m_myLabel = new Label() ;)在组件中是
构造函数,并使用委托和m_myLabel.Invoke(...)这样可以在原始线程中获得一个

同步调用。问题是,该组件没有

表格来创建标签表格。 window with,因此调用抛出一个异常

告诉我们一个不存在的窗口不支持Invoke。是吧。


问题:


a)如何在没有父表单的情况下创建窗口(不可见,

独立,仅适用于此代表),或者


b)是否有更好的方法告诉GC使用原始的主要?线程?


Christian

解决方案



" Christian Kaiser" < BC ** @ gmx.de>在消息中写道

news:eT ************** @ TK2MSFTNGP14.phx.gbl ...

|我们有一个没有窗口的组件。好吧,托管代码中没有窗口 -



|使用一个本身使用窗口的DLL,这就是我们的问题!

|


组件和DLL是如此通用的术语关于他们实际上是什么?b $ b,所以你必须更加明确。

究竟什么是组件?我想这是托管类。

DLL中包含什么,如何访问方法/函数?

通过PInvoke或COM互操作?

如果COM

- 什么是线程要求网络(公寓类型)?

- 线程的公寓状态是什么它被创建了吗?


|当垃圾收集器运行并删除我们的组件(创建

|动态,比如按钮点击,然后不再引用),



| GC在不同的线程中运行,禁止DLL销毁其

|窗口,当调用该窗口的WndProc时产生GPF -

|代码消失了(DLL卸载),但窗口仍然存在....

|


嗯...谁正在卸载DLL? GC没有卸载DLL'。

|清楚地看到(花了我们几个小时,说实话,了解是什么导致了

| GPF,因为如果与.NET 2.0一起使用,WinDBG会以某种方式使用100%的CPU负载),这就是$ /


|一个PITA和很多猜测。

|

|有没有办法解决这个问题,以便我们可以调用例程

|来自Dispose()在原始线程的上下文中?

|

|我们尝试在组件'

|中创建一个窗口(m_myLabel = new Label())。构造函数,并使用委托和m_myLabel.Invoke(...)这要得到一个

|在原始线程中同步调用。问题是,该组件有



|用于创建标签的表单窗口用,因此调用抛出异常

|告诉我们一个不存在的窗口不支持Invoke。对吧

是。

|

|问题:

|

| a)如何在没有父表单的情况下创建窗口(隐形,

|独立,仅适用于此委托),或

|


你不能,而且不需要,你的DLL代码会创建窗口,因此它的生命时间是负责的。

负责它的生命周期。


| b)有没有更好的方法告诉GC使用原始的主要?线程?

|


不,这不是问题无论如何,GC不知道不受管理

东西。

我猜你的问题与COM互操作的线程问题有关,但我当然可能是错误的b $ b。


Willy。


Willy,

好​​的,对不起,回到原点,我会尝试更具体; - )


该组件是一个托管C#控件。它本身通过PInvoke控制对DLL的调用

(用Delphi编写,但这本身不是基本问题)。

所以C#类应该是一个代理对于DLL的函数,还有一些或者更少的b / b
它的函数包装更少。


创建组件时(在主线程中,让'它称之为线程

A),它加载DLL,然后创建一个窗口(它需要,不幸的是,
)。它在DLL的LoadLibrary之后,在DLL的

启动代码中(同时初始化静态变量,......)。


问题当组件被Garbage

收集器处理时,它具有不同的线程B。组件的Dispose()

卸载DLL,它试图破坏它的窗口。现在这就是

问题 - 由线程B调用的DLL。无法破坏在

线程A中创建的窗口。在这里,我认为这是Delphi的问题,但问题是

或多或少在其他语言中也有效。 DestroyWindow()不起作用,

发送WM_CLOSE仍然让窗口生存,... - > GPF。


我们当时考虑的解决方案是在我们的组件中创建一个窗口

(例如使用Label)能够从线程B切换to thread

" A"以标准方式 - 同步访问控件的功能。所以在

的构造函数中,组件想要创建标签窗口,并且在

Dispose()方法中它使用标签窗口来同步FreeLibrary()

通过调用一个委托,该委托调用标签的事件,反过来(因为那时它在线程A中是'b
')可以释放DLL,因为它应该是。选秀(来自

记忆,我现在在家):


委托空委托KillDLL();


class component

{

void KillDLL()

{

//免费外部DLL ...

}


组件()

{

myLabel = new Label();

//加载外部DLL。

}


void Dispose()

{

DelegateKillDLL dlg = new DelegateKillDLL(KillDLL);

myLabel.Invoke(dlg,object [] {});

}

}


a)Invoke()调用不起作用,因为new Label()没有创建一个

窗口。所以Invoke()抛出一个例外,它没有任何

窗口就无法工作。有没有办法为这样的目的创建一个窗口(WS_OVERLAPPED

样式,可以这么说)?我们的组件不是视觉组件。控制(它没有

有任何窗口,不是从可视组件派生的,容器

属性为空)。

b)整个想法会在GC线程中运行吗?


c)有更好的方法吗?


我希望我能够比以前更好地描述它。如果有任何

的问题,请不要犹豫。我对C#比较陌生(但是非常有价值,但是在C ++,Win32中经验丰富...)b / b
Christian

" Willy Denoyette [MVP] QUOT; <无线************* @ telenet.be>在留言中写道

新闻:OE ************** @ tk2msftngp13.phx.gbl ...


" ; Christian Kaiser < BC ** @ gmx.de>在消息中写道
新闻:eT ************** @ TK2MSFTNGP14.phx.gbl ...
|我们有一个没有窗口的组件。好吧,没有窗口管理
代码 -

|使用一个本身使用窗口的DLL,这就是我们的问题!
|

组件和DLL是如此通用的术语,它们实际上很少说明它们实际上是什么是的,所以你必须更明确。
什么是组件?我想这是一个托管类。
DLL包含什么,你如何访问方法/函数?
通过PInvoke或COM互操作?
如果COM
- 什么是线程要求网(公寓类型)?
- 创建它的线程的公寓状态是什么?

|当垃圾收集器运行并删除我们的组件(通过按钮点击动态创建
|然后不再引用),

GC在不同的线程中运行,禁止DLL破坏它的
|窗口,当调用该窗口的WndProc时产生GPF -

|代码消失了(DLL卸载),但窗口仍然存在......
|

嗯......谁正在卸载DLL? GC没有卸载DLL'。

|清楚地看到(花了我们几个小时,说实话,了解是什么原因造成了什么?因为如果与.NET 2.0一起使用,WinDBG会以某种方式使用100%的CPU负载),这是
|一个PITA和很多猜测。
|
|有没有办法解决这个问题,以便我们可以调用例程
|来自Dispose()在原始线程的上下文中?
|
|我们尝试在
组件中创建一个窗口(m_myLabel = new Label())
|构造函数,并使用委托和m_myLabel.Invoke(...)这要获得
一个
在原始线程中同步调用。问题是,组件
没有
|用于创建标签的表单窗口用,因此调用抛出异常
|告诉我们一个不存在的窗口不支持Invoke。对吧
是。
|
|问题:
|
| a)如何在没有父表单的情况下创建窗口(不可见,
|独立,仅适用于此委托),或者
|

你不能,它不需要,你的DLL代码创建了窗口,因此它对它的生命周期负责。

| b)有没有更好的方法告诉GC使用原始的主要线程?
|

不,这不是问题无论如何,GC不知道
非托管
的东西。
我猜你的问题与COM互操作的线程问题有关,但我当然可能是错的。
<威利。



" Christian Kaiser" < BC ** @ gmx.de>在消息中写道

news:eT ************** @ TK2MSFTNGP14.phx.gbl ...

当垃圾收集器运行时并删除我们的组件(通过按钮点击动态创建,然后不再引用),
GC在不同的线程中运行,禁止DLL破坏它
窗口,当调用该窗口的WndProc时导致GPF -
代码消失(DLL卸载),但窗口仍然存在....




我想知道几件事情,


您使用的是Dispose()吗?也许你可以处理加载了这个DLL的线程,然后阻止处理如果发生在GC

(GC.SuppressFinalize)。


或者,也许你可以使用不同的appdomain加载东西,然后稍后丢弃

appdomain?


- Alan


We have a component that has no window. Well, no window in managed code - it
uses a DLL which itself uses a window, and this is our problem!

When the garbage collector runs and removes our component (created
dynamically by, say, a button click, and then not referenced any more), the
GC runs in a different thread, which prohibits the DLL to destroy its
window, resulting in a GPF when the WndProc of that window is called - the
code is gone (DLL unloaded), but the window still there....

Clear to see (took us hours, to be honest, to understand what caused the
GPF, as WinDBG somehow uses 100% CPU load if used with .NET 2.0), which was
a PITA and a lot of guesswork.

Is there a way to get around that problem, so that we can call a routine
from Dispose() in the context of the original thread?

We tried creating a window ("m_myLabel = new Label()") in the component''s
constructor, and use a delegate and "m_myLabel.Invoke(...)" this to get a
synchroneous invoke in the original thread. Problem is, the component has no
form to create the "Label" window with, thus invoke throws an exception
telling us that a non-existing window does not support Invoke. Right it is.

Questions:

a) How can a window be created without a parent form (invisible,
independent, just for this delegate), or

b) is there a better way to tell the GC to use the original "main" thread?

Christian

解决方案


"Christian Kaiser" <bc**@gmx.de> wrote in message
news:eT**************@TK2MSFTNGP14.phx.gbl...
| We have a component that has no window. Well, no window in managed code -
it
| uses a DLL which itself uses a window, and this is our problem!
|

Components and DLL''s are such general terms that say so little about what
they actually are, so you will have to be more explicit.
What''s exactly the component? I guess it''s a managed class.
What''s contained in the DLL, how do you access the methods/functions?
Through PInvoke or COM interop?
If COM
- what are it''s threading requiremnets (apartment type)?
- What is the apartment state of the thread on which it is created?

| When the garbage collector runs and removes our component (created
| dynamically by, say, a button click, and then not referenced any more),
the
| GC runs in a different thread, which prohibits the DLL to destroy its
| window, resulting in a GPF when the WndProc of that window is called - the
| code is gone (DLL unloaded), but the window still there....
|

Hmmm... who''s unloading the DLL? The GC doesn''t unload DLL''s.
| Clear to see (took us hours, to be honest, to understand what caused the
| GPF, as WinDBG somehow uses 100% CPU load if used with .NET 2.0), which
was
| a PITA and a lot of guesswork.
|
| Is there a way to get around that problem, so that we can call a routine
| from Dispose() in the context of the original thread?
|
| We tried creating a window ("m_myLabel = new Label()") in the component''s
| constructor, and use a delegate and "m_myLabel.Invoke(...)" this to get a
| synchroneous invoke in the original thread. Problem is, the component has
no
| form to create the "Label" window with, thus invoke throws an exception
| telling us that a non-existing window does not support Invoke. Right it
is.
|
| Questions:
|
| a) How can a window be created without a parent form (invisible,
| independent, just for this delegate), or
|

You can''t, and it''s not needed, your DLL code creates the window so it''s
responsible for it''s life time.

| b) is there a better way to tell the GC to use the original "main" thread?
|

No, and this is not the problem anyway, the GC doesn''t know about unmanaged
stuff.
I guess your problem relates to threading issues with COM interop, but I
could be wrong of course.

Willy.


Hi Willy,
OK, sorry, back to square one, I''ll try to be more specific ;-)

The component is a managed C# control. It itself controls calls to a DLL
(written in Delphi, but that itself is not the basic problem) via PInvoke.
So the C# class is meant to be a proxy to the DLL''s functions, some more or
less slim wrapper around its functions.

When the component is created (in the main thread, let''s call it thread
"A"), it loads the DLL, which in turn then creates a window (it needs to,
unfortunately). It does so right after LoadLibrary of the DLL, in the
startup code of the DLL (while initializing static variables, ...).

The problem comes when the component is disposed of by the Garbage
Collector, which has a different thread "B". The Dispose() of the component
unloads the DLL, which tries to destroy its window. And this now is the
problem - the DLL called by thread "B" cannot destroy the window created in
thread "A". Here it''s the problem of Delphi, I assume, but the problem is
more or less valid in other languages too. DestroyWindow() does not work,
sendin a WM_CLOSE still lets the window survive, ... -> GPF.

The solution we thought about then was to create a window in our component
(using a "Label" for example) to be able to switch from thread "B" to thread
"A" in a standard way - synchronous access to a control''s functions. So in
its constructor, the component wants to create the label window, and in the
Dispose() method it uses the label window to synchronize the FreeLibrary()
by invoking a delegate, which calls an event of the label, which in turn (as
it''s in thread A by then) can free the DLL as it should be. Draft (from
memory, I''m at home now):

delegate void DelegateKillDLL();

class component
{
void KillDLL()
{
// Free external DLL...
}

component()
{
myLabel = new Label();
// Load external DLL.
}

void Dispose()
{
DelegateKillDLL dlg = new DelegateKillDLL(KillDLL);
myLabel.Invoke(dlg, object[] {});
}
}

a) the Invoke() call does not work, as "new Label()" did not create a
window. So "Invoke()" throws an exception that it cannot work without any
window. Is there a way to create a window for such a purpose (WS_OVERLAPPED
style, so to speak)? Our component is not a "visual" control (it does not
have any window, is not derived from a visual component, the "Container"
property is null).

b) will the whole idea work in a GC thread?

c) Is there a better way to do this?

I hope I was able to describe it better than before. If there''s any
question, don''t hesitate to ask. I''m relatively new to C# (but very
experienced in C++, Win32, ...)
Christian
"Willy Denoyette [MVP]" <wi*************@telenet.be> wrote in message
news:OE**************@tk2msftngp13.phx.gbl...


"Christian Kaiser" <bc**@gmx.de> wrote in message
news:eT**************@TK2MSFTNGP14.phx.gbl...
| We have a component that has no window. Well, no window in managed
code -
it
| uses a DLL which itself uses a window, and this is our problem!
|

Components and DLL''s are such general terms that say so little about what
they actually are, so you will have to be more explicit.
What''s exactly the component? I guess it''s a managed class.
What''s contained in the DLL, how do you access the methods/functions?
Through PInvoke or COM interop?
If COM
- what are it''s threading requiremnets (apartment type)?
- What is the apartment state of the thread on which it is created?

| When the garbage collector runs and removes our component (created
| dynamically by, say, a button click, and then not referenced any more),
the
| GC runs in a different thread, which prohibits the DLL to destroy its
| window, resulting in a GPF when the WndProc of that window is called -
the
| code is gone (DLL unloaded), but the window still there....
|

Hmmm... who''s unloading the DLL? The GC doesn''t unload DLL''s.
| Clear to see (took us hours, to be honest, to understand what caused the
| GPF, as WinDBG somehow uses 100% CPU load if used with .NET 2.0), which
was
| a PITA and a lot of guesswork.
|
| Is there a way to get around that problem, so that we can call a routine
| from Dispose() in the context of the original thread?
|
| We tried creating a window ("m_myLabel = new Label()") in the
component''s
| constructor, and use a delegate and "m_myLabel.Invoke(...)" this to get
a
| synchroneous invoke in the original thread. Problem is, the component
has
no
| form to create the "Label" window with, thus invoke throws an exception
| telling us that a non-existing window does not support Invoke. Right it
is.
|
| Questions:
|
| a) How can a window be created without a parent form (invisible,
| independent, just for this delegate), or
|

You can''t, and it''s not needed, your DLL code creates the window so it''s
responsible for it''s life time.

| b) is there a better way to tell the GC to use the original "main"
thread?
|

No, and this is not the problem anyway, the GC doesn''t know about
unmanaged
stuff.
I guess your problem relates to threading issues with COM interop, but I
could be wrong of course.

Willy.



"Christian Kaiser" <bc**@gmx.de> wrote in message
news:eT**************@TK2MSFTNGP14.phx.gbl...

When the garbage collector runs and removes our component (created
dynamically by, say, a button click, and then not referenced any more),
the
GC runs in a different thread, which prohibits the DLL to destroy its
window, resulting in a GPF when the WndProc of that window is called - the
code is gone (DLL unloaded), but the window still there....



Couple of things I''m wondering,

Are you using Dispose()? Maybe you could dispose on the thread that loaded
the DLL, then prevent the dispose if happening from GC
(GC.SuppressFinalize).

Or, maybe you could load stuff using a different appdomain, then discard the
appdomain later?

-- Alan


这篇关于垃圾收集器的线程问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆