使用Invoke处理来自其他线程的调用。这是一个好模式吗?

时间:2014-01-11 21:26:13

标签: c# multithreading winforms invoke ui-thread

在阅读了如何使用Invoke来从其他线程更新GUI元素后,我对它进行了一些工作,并最终采用了以下方法来处理它。我相当确定我的解决方案过于复杂,但我相信它的工作正常。

我看到这种方法的优点是,一旦GUI线程准备就绪,它允许短时间连续存储多个命令以供消费,并保持命令的顺序。缺点是我存储这些临时参数看起来效率低下(我可以创建一个通用类来存储所有临时参数以将它们隐藏到单个对象中)

我选择为所有通话重复使用相同的互斥锁,但只要它们配对就可以使用不同的通道。

那么我可以使用其他模式来获得相同的结果? (希望用一种不太复杂的方法。)

//The main form class.
public class GUIHandler
{
    Mutex InvokeOnce = new Mutex(); //Mutex that ensures that temp storage only gets written to or read from

    //tempData for Invoke Methodes
    List<SomeObject> invokeParameter = new List<SomeObject>();
    List<SomeOtherObject> anotherInvokeParameter = new List<SomeOtherObject>();

    public GUIHandler()
    {
        //Some code to initialize the GUI
    }   

    //Generic Reused InvokeDelegate
    public delegate void InvokeDelegate();

    //External Call with parameters different calls different names
    private void SomeInvokeRequiredAction(SomeObject someParameter)
    {
        //call mutex for handle the storage and store the parameter
        InvokeOnce.WaitOne();
        invokeParameter.Add(someParameter);
        InvokeOnce.ReleaseMutex();
        this.BeginInvoke(new InvokeDelegate(SomeInvokeRequiredActionInvoke));
    }

    //Invoked Code with related name to its primary external call
    private void SomeInvokeRequiredActionInvoke()
    {
        InvokeOnce.WaitOne();
        //some random action on a GUI element that required the Invoke in first place
        guiElement.Text = invokeParameter[0]
        invokeParameter.RemoveAt(0);

        InvokeOnce.ReleaseMutex();
    }
}

1 个答案:

答案 0 :(得分:1)

如果你需要的是:

  1. 将工作传递给UI线程。
  2. 保留该作品的订单。
  3. 您不需要简单的Invoke。它是线程安全的(因此不需要锁定)。它搜索最顶层的控件及其窗口句柄并排队等待它运行(消息循环)。因为它只是一个单一的线程,所以工作将按顺序完成。

    正如评论中提出的问题一样,像这样的简单解决方案可能就足够了:

    用法:

    button.InvokeIfRequired(() =>
    {
        // This will run under the UI thread.
        button.Text = "hamster";
    });
    

    实现:

    public static void InvokeIfRequired(this ISynchronizeInvoke control, MethodInvoker action)
    {
        if (control.InvokeRequired)
        {
            control.Invoke(action);
        }
        else
        {
            action();
        }
    }