C#多线程 - 在线程之间移动对象

Gra*_*ant 9 c# multithreading winforms

我正在使用一个winforms控件,它既是一个GUI元素,也是一些尚未向开发人员公开的内部处理.当这个组件被实例化时,它可能需要5到15秒才能准备就绪,所以我想做的就是将它放在另一个线程上,当它完成时将它带回gui线程并将其放在我的表单上.问题是这会(并且有)导致跨线程异常.

通常,当我使用工作线程时,它只使用简单的数据对象,我可以在处理完成后推回,然后使用已经在主线程上的控件,但我从来不需要以这种方式移动整个控件.

有谁知道这是否可能,如果可能,如何?如果不是如何处理这样的问题,哪里有可能锁定主gui?

Kir*_*ril 3

你不需要锁定GUI,你只需要调用invoke:

Windows 窗体中的控件绑定到特定线程,并且不是线程安全的。因此,如果您从不同的线程调用控件的方法,则必须使用控件的调用方法之一将调用封送到正确的线程。此属性可用于确定是否必须调用调用方法,如果您不知道哪个线程拥有控件,这会很有用。参考

代码如下:

public delegate void ComponentReadyDelegate(YourComponent component);
public void LoadComponent(YourComponent component)
{
    if (this.InvokeRequired)
    {
        ComponentReadyDelegate e = new ComponentReadyDelegate(LoadComponent);
        this.BeginInvoke(e, new object[]{component});
    }
    else
    {
        // The component is used by a UI control
        component.DoSomething();
        component.GetSomething();
    }
}

// From the other thread just initialize the component
// and call the LoadComponent method on the GUI.
component.Initialize(); // 5-15 seconds
yourForm.LoadComponent(component);
Run Code Online (Sandbox Code Playgroud)

通常从另一个线程调用LoadComponent会导致跨线程异常,但通过上述实现,该方法将在 GUI 线程上调用。

InvokeRequired告诉你如果:

调用者在对控件进行方法调用时必须调用调用方法,因为调用者与创建控件的线程位于不同的线程上。 参考

更新:
所以,如果我理解正确的话,控制对象是在 GUI 线程以外的线程上创建的,因此,即使您能够将它传递给 GUI 线程,您仍然无法在不导致交叉的情况下使用它。线程异常。解决方案是在 GUI 线程上创建对象,但在单独的线程上初始化它:

public partial class MyForm : Form
{
    public delegate void ComponentReadyDelegate(YourComponent component);
    private YourComponent  _component;
    public MyForm()
    {
        InitializeComponent();
        // The componet is created on the same thread as the GUI
        _component = new YourComponent();

        ThreadPool.QueueUserWorkItem(o =>
        {
            // The initialization takes 5-10 seconds
            // so just initialize the component in separate thread
            _component.Initialize();

            LoadComponent(_component);
        });
    }

    public void LoadComponent(YourComponent component)
    {
        if (this.InvokeRequired)
        {
            ComponentReadyDelegate e = new ComponentReadyDelegate(LoadComponent);
            this.BeginInvoke(e, new object[]{component});
        }
        else
        {
            // The component is used by a UI control
            component.DoSomething();
            component.GetSomething();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)