【问题标题】:Initialise instance of a form on a different thread?在不同的线程上初始化表单的实例?
【发布时间】:2009-08-14 05:53:10
【问题描述】:

担心

在 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: 

问题

是否可以在主应用程序线程以外的线程上创建表单实例,还是我必须将 all 回调编组回该线程?这在我升级 Telerik 控件之前一直有效,所以要么他们现在有一个新错误,要么我一直做错了。

请帮忙!

【问题讨论】:

  • 表格有什么用?

标签: c# winforms multithreading


【解决方案1】:

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

回答您的问题:是的,您正在做的事情是可以接受的,但这样的用例非常罕见,以至于 3rdparty 控件的供应商可能决定不支持它。对它们(或者如果你有源代码)最简单的解决方法是定位全局变量并使它们成为线程局部变量,如下所示:

[ThreadStatic]
static int foo;

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

【讨论】:

  • 啊。不幸的是,我没有源代码,所以看起来我只需要更改我的应用程序,以便所有表单都在同一个线程上创建。哦,好吧...
【解决方案2】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-05
    相关资源
    最近更新 更多