在不同的线程上初始化表单的实例?

时间:2009-08-13 14:07:44

标签: c# winforms multithreading

担忧

在WinForms中主线程以外的线程上创建控件/表单是不好的做法?我认为规则只是一个线程无法改变在另一个线程上创建的控件上的某些东西?

场景

我有一些带有一些工作线程的应用程序。这些工作线程可以各自显示他们自己的表单实例。发生在该表单中的所有内容都保留在该线程中,没有其他线程会尝试对该表单实例执行任何操作。表单有一些Telerik控件,自从将它们升级到新版本后,我得到了这里描述的错误类型:

System.Reflection.TargetInvocationException was unhandled
  Message="Exception has been thrown by the target of an invocation."
  Source="mscorlib"
  StackTrace:
       at System.RuntimeMethodHandle._InvokeMethodFast(Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)
       at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
       at System.Delegate.DynamicInvokeImpl(Object[] args)
       at System.Windows.Forms.Control.InvokeMarshaledCallbackDo(ThreadMethodEntry tme)
       at System.Windows.Forms.Control.InvokeMarshaledCallbackHelper(Object obj)
       at System.Threading.ExecutionContext.runTryCode(Object userData)
       at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Windows.Forms.Control.InvokeMarshaledCallback(ThreadMethodEntry tme)
       at System.Windows.Forms.Control.InvokeMarshaledCallbacks()
       at System.Windows.Forms.Control.WndProc(Message& m)
       at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
       at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
       at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
       at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)
       at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
       at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
       at TelerikControlsTest.Program.Main() in C:\Data Files\Projects\Test Projects\TelerikControlsTest\TelerikControlsTest\Program.cs:line 18
       at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException: System.InvalidOperationException
       Message="Object is currently in use elsewhere."
       Source="System.Drawing"
       StackTrace:
            at System.Drawing.Image.get_FrameDimensionsList()
            at System.Drawing.ImageAnimator.CanAnimate(Image image)
            at Telerik.WinControls.Primitives.ImagePrimitive.get_Image()
            at Telerik.WinControls.Primitives.ImagePrimitive.GetImage()
            at Telerik.WinControls.Primitives.ImagePrimitive.get_PreferredImageSize()
            at Telerik.WinControls.Primitives.ImagePrimitive.MeasureOverride(SizeF availableSize)
            at Telerik.WinControls.RadElement.MeasureCore(SizeF availableSize)
            at Telerik.WinControls.RadElement.Measure(SizeF availableSize)
            at Telerik.WinControls.Layouts.ContextLayoutManager.UpdateLayout()
       InnerException: 

问题

在主应用程序线程以外的线程上创建表单实例是否可以接受,或者我是否必须将所有调用重新编组回该线程?这曾经工作,直到我升级了Telerik控件,所以他们现在有一个新的错误,或者我一直在做错。

请帮忙!

2 个答案:

答案 0 :(得分:2)

从堆栈跟踪中,看起来Telerik控件正在共享一个System.Drawing.Image实例,可能通过一些全局(静态)变量。因为您在不同的线程上实例化控件,所以最终会同时从多个线程访问该图像,这是不受支持的。我们在使用全局变量的一些遗留代码(非GUI)时遇到了类似的问题。

回答你的问题:是的,你正在做的事情是可以接受的,但这样的用例非常罕见,以至于第三方控件的供应商可能决定不支持它。对他们来说最简单的修复(或者如果你有源代码的话)是找到全局变量并使它们像这样线程局部:

[ThreadStatic]
static int foo;

这将确保每个线程获得自己的“全局”变量,将它们彼此隔离。

答案 1 :(得分:1)

您需要在尝试显示表单的每个线程上调用Application.Run()(或其中一个重载)。除此之外,只要您不尝试跨线程更改内容,就应该没问题。

值得注意的是,如果您使用Application.Run(Form)重载,那么当表单关闭时,在函数调用后继续执行。