将线程安全访问方法写入Windows窗体控件的最短方法

时间:2009-02-20 23:54:15

标签: c# thread-safety

在本文中:

http://msdn.microsoft.com/en-us/library/ms171728(VS.80).aspx

作者使用以下方法对Windows窗体控件进行线程安全调用:

private void SetText(string text)
{
    // InvokeRequired required compares the thread ID of the
    // calling thread to the thread ID of the creating thread.
    // If these threads are different, it returns true.
    if (this.textBox1.InvokeRequired)
    {    
        SetTextCallback d = new SetTextCallback(SetText);
        this.Invoke(d, new object[] { text });
    }
    else
    {
        this.textBox1.Text = text;
    }
}

有没有更短的方法来完成同样的事情?

5 个答案:

答案 0 :(得分:34)

C#3.0以及之后:

扩展方法通常是要走的路,因为你总是希望对ISynchronizeInvoke interface实现执行操作,这是一个很好的设计选择。

您还可以利用anonymous methods(闭包)来解释您不知道要传递给扩展方法的参数的事实;关闭将捕获所需的一切状态。

// Extension method.
static void SynchronizedInvoke(this ISynchronizeInvoke sync, Action action)
{
    // If the invoke is not required, then invoke here and get out.
    if (!sync.InvokeRequired)
    {
        // Execute action.
        action();

        // Get out.
        return;
    }

    // Marshal to the required context.
    sync.Invoke(action, new object[] { });
}

然后你会这样称呼它:

private void SetText(string text)
{
    textBox1.SynchronizedInvoke(() => textBox1.Text = text);
}

此处,闭包位于text参数之上,该状态被捕获并作为传递给扩展方法的Action delegate的一部分传递。

在C#3.0之前:

您没有lambda表达式的奢侈品,但您仍然可以概括代码。它几乎相同,但不是扩展方法:

static void SynchronizedInvoke(ISynchronizeInvoke sync, Action action)
{
    // If the invoke is not required, then invoke here and get out.
    if (!sync.InvokeRequired)
    {
        // Execute action.
        action();

        // Get out.
        return;
    }

    // Marshal to the required context.
    sync.Invoke(action, new object[] { });
}

然后用匿名方法语法调用它:

private void SetText(string text)
{
    SynchronizedInvoke(textBox1, delegate() { textBox1.Text = text; });
}

答案 1 :(得分:8)

1)使用匿名代表

private void SetText(string text)
{
    if (this.InvokeRequired)
    {    
        Invoke(new MethodInvoker(delegate() {
            SetText(text);
        }));
    }
    else
    {
        this.textBox1.Text = text;
    }
}

2)AOP方法

[RunInUIThread]
private void SetText(string text)
{
    this.textBox1.Text = text;
}

http://weblogs.asp.net/rosherove/archive/2007/05.aspx?PageIndex=2

3)使用lambda表达式(由他人概述)。

答案 2 :(得分:4)

编辑:我应该提到我不认为这是最佳实践

如果您使用的是3.5,则可以使用扩展方法:

public static void SafeInvoke(this Control control, Action handler) {
    if (control.InvokeRequired) {
        control.Invoke(handler);
    }
    else {
        handler();
    }
}

这基本上来自:Here

然后使用它:

textBox1.SafeInvoke(() => .... );

当然要为您的用法修改扩展名等。

答案 3 :(得分:1)

这对大多数人来说可能是显而易见的,但如果您需要检索值,您可以接受已接受的答案并添加另一种方法......

public static T SynchronizedFunc<T>(this ISynchronizeInvoke sync, Func<T> func)
{
    if (!sync.InvokeRequired)
    {
        // Execute the function
        return func();
    }

    // Marshal onto the context
    return (T) sync.Invoke(func, new object[] { });
}

我最近用这个以线程安全的方式处理表单......

var handle = f.SynchronizedFunc(() => f.Handle);

答案 4 :(得分:0)

我找到的最短解决方案显示在下面的按钮示例中,其目标是更改按钮的文本。

    if (buttonX.InvokeRequired)
        buttonX.Invoke((Action)(() => buttonX.Text = "Record"));
    else
        buttonX.Text = "Record";